From 56427ed217304ecbbc941acfb399f564fccb6b28 Mon Sep 17 00:00:00 2001 From: Cristian Bujoreanu Date: Tue, 14 Apr 2015 16:33:44 +0200 Subject: [PATCH 01/29] migrate to Extjs 5 --- Extensible-config.js | 88 ++-- examples/calendar/data/EventsCustom.js | 26 +- examples/calendar/doc-types.js | 1 - examples/calendar/localization.js | 1 - examples/calendar/remote/recurrence.js | 67 ++- examples/calendar/remote/remote.js | 47 +- examples/calendar/window.js | 2 +- examples/examples.js | 4 +- examples/server/php/api/events-common.php | 2 +- examples/server/php/api/events-recurrence.php | 15 +- resources/css/calendar.css | 41 +- src/calendar/CalendarPanel.js | 467 +++++++++--------- src/calendar/data/EventMappings.js | 44 +- src/calendar/data/MemoryCalendarStore.js | 4 +- src/calendar/data/MemoryEventStore.js | 74 ++- src/calendar/dd/DropZone.js | 4 +- src/calendar/dd/StatusProxy.js | 26 +- src/calendar/form/EventDetails.js | 74 +-- src/calendar/form/EventWindow.js | 106 ++-- src/calendar/gadget/CalendarListMenu.js | 16 +- src/calendar/gadget/CalendarListPanel.js | 6 +- src/calendar/menu/Event.js | 104 ++-- src/calendar/view/AbstractCalendar.js | 428 ++++++++-------- src/calendar/view/DayBody.js | 72 +-- src/calendar/view/Month.js | 62 +-- src/calendar/view/MonthDayDetail.js | 4 - src/data/Model.js | 17 +- src/form/recurrence/AbstractOption.js | 24 +- src/form/recurrence/Fieldset.js | 24 +- src/form/recurrence/FrequencyCombo.js | 14 +- src/form/recurrence/option/Interval.js | 20 +- src/form/recurrence/option/Weekly.js | 2 +- 32 files changed, 1011 insertions(+), 875 deletions(-) diff --git a/Extensible-config.js b/Extensible-config.js index 2aa58c01..a80601b3 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,26 @@ 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. - * + * * 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', - + mode: 'dynamic', + /** * 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 +61,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 +85,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 +103,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 +119,44 @@ 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 the default theme + * Based by theme nema the path to resouces is built: ext-5.1.0\build\packages\ext-theme-$THEME + */ + 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,28 +164,28 @@ 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' @@ -186,29 +193,28 @@ Extensible.Config = { // after that, so use the version number instead of a unique string cacheBuster = '?_dc=' + Extensible.version; break; - + default: + suffixExt = '-all-debug'; // 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'; } } - - me.includeStylesheet(me.extJsRoot + 'resources/css/ext-all.css'); + + me.includeStylesheet(me.extJsRoot + '/build/packages/ext-theme-' + me.theme + '/build/resources/ext-theme-' + me.theme + '-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.extJsRoot + 'build/' + '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/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..e1cba78f 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; } } diff --git a/examples/calendar/localization.js b/examples/calendar/localization.js index 2270d14d..8de026cd 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; diff --git a/examples/calendar/remote/recurrence.js b/examples/calendar/remote/recurrence.js index 0cca44a7..ec28d0aa 100644 --- a/examples/calendar/remote/recurrence.js +++ b/examples/calendar/remote/recurrence.js @@ -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] : ''; + }); + + return postData; + }, + scope: this + } } }, @@ -114,8 +157,10 @@ 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 = operation.getRequest().getJsonData(), + title = record[Extensible.calendar.data.EventMappings.Title.mapping] || '(No title)'; + switch(operation.action){ case 'create': Extensible.example.msg('Add', 'Added "' + title + '"'); diff --git a/examples/calendar/remote/remote.js b/examples/calendar/remote/remote.js index df53ccad..6bca9cd7 100644 --- a/examples/calendar/remote/remote.js +++ b/examples/calendar/remote/remote.js @@ -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] : ''; + }); + + return postData; + }, + scope: this + } } }, @@ -98,8 +131,10 @@ 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 = operation.getRequest().getJsonData(), + title = record[Extensible.calendar.data.EventMappings.Title.mapping] || '(No title)'; + switch(operation.action){ case 'create': Extensible.example.msg('Add', 'Added "' + title + '"'); diff --git a/examples/calendar/window.js b/examples/calendar/window.js index 87d651fd..fa401ed0 100644 --- a/examples/calendar/window.js +++ b/examples/calendar/window.js @@ -36,7 +36,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..0aabdc09 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; @@ -763,14 +762,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 +778,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 +957,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 +992,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/src/calendar/CalendarPanel.js b/src/calendar/CalendarPanel.js index 002b6130..bb112306 100644 --- a/src/calendar/CalendarPanel.js +++ b/src/calendar/CalendarPanel.js @@ -312,238 +312,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) { @@ -720,7 +720,7 @@ Ext.define('Extensible.calendar.CalendarPanel', { }, onWrite: function(store, operation) { - var rec = operation.records[0]; + var rec = operation.getRequest().getJsonData(); switch(operation.action) { case 'create': @@ -827,7 +827,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) { 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/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..e44262cc 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,18 @@ 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 record = operation.getRequest().getJsonData(), + title = record.data[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 +136,49 @@ 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 }) + }); + + 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 }) + }); + + 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 }) + }); + + 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..f609247c 100644 --- a/src/calendar/form/EventWindow.js +++ b/src/calendar/form/EventWindow.js @@ -95,57 +95,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); @@ -371,7 +371,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 +427,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..904480a7 100644 --- a/src/calendar/gadget/CalendarListPanel.js +++ b/src/calendar/gadget/CalendarListPanel.js @@ -133,7 +133,7 @@ Ext.define('Extensible.calendar.gadget.CalendarListPanel', { 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) { @@ -160,11 +160,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{ 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/view/AbstractCalendar.js b/src/calendar/view/AbstractCalendar.js index 1053729d..dd8ad310 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,12 @@ 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 record = operation.getRequest().getJsonData(), + RInstanceStartDate = Extensible.calendar.data.EventMappings.RInstanceStartDate, + isInstance = RInstanceStartDate && !!record[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 +990,7 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { this.refreshAfterEventChange('update', operation); - var rec = operation.records[0]; + var rec = operation.getRequest().getJsonData(); if (this.enableFx && this.enableUpdateFx) { this.doUpdateFx(this.getEventEls(rec.data[Extensible.calendar.data.EventMappings.EventId.name]), { @@ -1012,7 +1015,7 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { }, onAdd: function(store, operation) { - var rec = operation.records[0]; + var rec = operation.getRequest().getJsonData(); 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,10 +1067,10 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { Extensible.log('onRemove'); this.dismissEventEditor(); - var rec = operation.records[0]; + var rec = operation.getRequest().getJsonData(); if (this.enableFx && this.enableRemoveFx) { - this.doRemoveFx(this.getEventEls(rec.data[Extensible.calendar.data.EventMappings.EventId.name]), { + this.doRemoveFx(this.getEventEls(rec[Extensible.calendar.data.EventMappings.EventId.name]), { remove: true, scope: this, callback: Ext.bind(this.refreshAfterEventChange, this, ['delete', operation]) @@ -1761,8 +1764,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); @@ -1878,7 +1879,8 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { newRec = rec; } else { - newRec = rec.clone(); + newRec = rec.copy(null); + newRec.set(Extensible.calendar.data.EventMappings.EventId.mapping, null); } if (me.fireEvent('beforeevent' + moveOrCopy, me, newRec, Ext.Date.clone(newStartDate)) !== false) { diff --git a/src/calendar/view/DayBody.js b/src/calendar/view/DayBody.js index c1ef35a4..0ee27a22 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() { 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..b5739347 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,17 @@ 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 { + proto.fields.push(Ext.create('Ext.data.field.Field', fields[i])); + } } return this; } 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; From 53835dd5849c0b4e297c71edda25f0dd2444c6d0 Mon Sep 17 00:00:00 2001 From: Cristian Bujoreanu Date: Wed, 15 Apr 2015 17:42:11 +0200 Subject: [PATCH 02/29] fix copy event issue and update handler for onWrite event --- src/calendar/CalendarPanel.js | 4 ++-- src/calendar/data/MemoryEventStore.js | 14 +++++++---- src/calendar/form/EventWindow.js | 6 ++--- src/calendar/view/AbstractCalendar.js | 34 +++++++++++++++------------ 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/calendar/CalendarPanel.js b/src/calendar/CalendarPanel.js index bb112306..4b62830d 100644 --- a/src/calendar/CalendarPanel.js +++ b/src/calendar/CalendarPanel.js @@ -720,8 +720,8 @@ Ext.define('Extensible.calendar.CalendarPanel', { }, onWrite: function(store, operation) { - var rec = operation.getRequest().getJsonData(); - + var records = operation.getRecords(), rec = records[0]; + switch(operation.action) { case 'create': this.onStoreAdd(store, rec); diff --git a/src/calendar/data/MemoryEventStore.js b/src/calendar/data/MemoryEventStore.js index e44262cc..076a98c7 100644 --- a/src/calendar/data/MemoryEventStore.js +++ b/src/calendar/data/MemoryEventStore.js @@ -93,8 +93,9 @@ Ext.define('Extensible.calendar.data.MemoryEventStore', { var me = this; if (Extensible.example && Extensible.example.msg) { - var record = operation.getRequest().getJsonData(), - title = record.data[Extensible.calendar.data.EventMappings.Title.mapping] || '(No title)'; + 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': @@ -149,7 +150,8 @@ Ext.define('Extensible.calendar.data.MemoryEventStore', { var operation = Ext.create('Ext.data.operation.Create',{ success: true, complete: true, - request: Ext.create('Ext.data.Request', { jsonData: record }) + request: Ext.create('Ext.data.Request', { jsonData: record }), + records: [record] }); store.fireAction('write', [store, operation], function(){}); @@ -160,7 +162,8 @@ Ext.define('Extensible.calendar.data.MemoryEventStore', { var operation = Ext.create('Ext.data.operation.Update',{ success: true, complete: true, - request: Ext.create('Ext.data.Request', { jsonData: record }) + request: Ext.create('Ext.data.Request', { jsonData: record }), + records: [record] }); store.fireAction('write', [store, operation], function(){}); @@ -173,7 +176,8 @@ Ext.define('Extensible.calendar.data.MemoryEventStore', { var operation = Ext.create('Ext.data.operation.Destroy',{ success: true, complete: true, - request: Ext.create('Ext.data.Request', { jsonData: record }) + request: Ext.create('Ext.data.Request', { jsonData: record }), + _resultSet: Ext.create('Ext.data.ResultSet', { records: [record]}) }); store.fireAction('write', [store, operation], function(){}); diff --git a/src/calendar/form/EventWindow.js b/src/calendar/form/EventWindow.js index f609247c..cb7a4cb1 100644 --- a/src/calendar/form/EventWindow.js +++ b/src/calendar/form/EventWindow.js @@ -351,9 +351,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) { diff --git a/src/calendar/view/AbstractCalendar.js b/src/calendar/view/AbstractCalendar.js index dd8ad310..1a41aa3d 100644 --- a/src/calendar/view/AbstractCalendar.js +++ b/src/calendar/view/AbstractCalendar.js @@ -962,9 +962,10 @@ 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 record = operation.getRequest().getJsonData(), + var records = operation.getRecords(), + record = records[0], RInstanceStartDate = Extensible.calendar.data.EventMappings.RInstanceStartDate, - isInstance = RInstanceStartDate && !!record[RInstanceStartDate.name], + isInstance = RInstanceStartDate && !!record.get(RInstanceStartDate.name), reload = isInstance && !operation.wasStoreReloadTriggered; //reload = (record.isRecurring() || isInstance) && !operation.wasStoreReloadTriggered; @@ -990,7 +991,7 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { this.refreshAfterEventChange('update', operation); - var rec = operation.getRequest().getJsonData(); + var records = operation.getRecords(), rec = records[0]; if (this.enableFx && this.enableUpdateFx) { this.doUpdateFx(this.getEventEls(rec.data[Extensible.calendar.data.EventMappings.EventId.name]), { @@ -1015,7 +1016,7 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { }, onAdd: function(store, operation) { - var rec = operation.getRequest().getJsonData(); + 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 @@ -1067,18 +1068,20 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { Extensible.log('onRemove'); this.dismissEventEditor(); - var rec = operation.getRequest().getJsonData(); + if (operation.getResultSet()){ + var records = operation.getResultSet().getRecords(), rec = records[0]; - if (this.enableFx && this.enableRemoveFx) { - this.doRemoveFx(this.getEventEls(rec[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); + } } }, @@ -1880,6 +1883,7 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { } else { newRec = rec.copy(null); + newRec.phantom = true; newRec.set(Extensible.calendar.data.EventMappings.EventId.mapping, null); } From cfda86a2d375821eb6721f30b66d09d07bee3091 Mon Sep 17 00:00:00 2001 From: Cristian Bujoreanu Date: Thu, 16 Apr 2015 09:38:09 +0200 Subject: [PATCH 03/29] fix issue in recurrence and remote examples --- examples/calendar/remote/recurrence.js | 12 ++++++++++-- examples/calendar/remote/remote.js | 12 ++++++++++-- src/calendar/view/AbstractCalendar.js | 2 +- src/data/Model.js | 2 ++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/examples/calendar/remote/recurrence.js b/examples/calendar/remote/recurrence.js index ec28d0aa..f7f0e19b 100644 --- a/examples/calendar/remote/recurrence.js +++ b/examples/calendar/remote/recurrence.js @@ -141,7 +141,7 @@ Ext.onReady(function() { // 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] : ''; + postData[value.mapping] = data[value.name] ? data[value.name] : null; }); return postData; @@ -158,8 +158,16 @@ Ext.onReady(function() { // option for generically messaging after CRUD persistence has succeeded. listeners: { write: function(store, operation) { - var record = operation.getRequest().getJsonData(), + 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': diff --git a/examples/calendar/remote/remote.js b/examples/calendar/remote/remote.js index 6bca9cd7..6fd78163 100644 --- a/examples/calendar/remote/remote.js +++ b/examples/calendar/remote/remote.js @@ -115,7 +115,7 @@ Ext.onReady(function() { // 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] : ''; + postData[value.mapping] = data[value.name] ? data[value.name] : null; }); return postData; @@ -132,8 +132,16 @@ Ext.onReady(function() { // option for generically messaging after CRUD persistence has succeeded. listeners: { write: function(store, operation) { - var record = operation.getRequest().getJsonData(), + 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': diff --git a/src/calendar/view/AbstractCalendar.js b/src/calendar/view/AbstractCalendar.js index 1a41aa3d..2b04ab61 100644 --- a/src/calendar/view/AbstractCalendar.js +++ b/src/calendar/view/AbstractCalendar.js @@ -962,7 +962,7 @@ 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 records = operation.getRecords(), + 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), diff --git a/src/data/Model.js b/src/data/Model.js index b5739347..11d9fa65 100644 --- a/src/data/Model.js +++ b/src/data/Model.js @@ -60,6 +60,8 @@ Ext.define('Extensible.data.Model', { 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])); } From 37a57a22077e0e69e84f2cf45ab6e6b3cfb0d681 Mon Sep 17 00:00:00 2001 From: Cristian Bujoreanu Date: Thu, 16 Apr 2015 18:10:02 +0200 Subject: [PATCH 04/29] fix issue related to event handler of an copied event --- src/calendar/data/EventModel.js | 2 +- src/calendar/view/AbstractCalendar.js | 22 +++++++++++----------- src/data/Model.js | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) 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/view/AbstractCalendar.js b/src/calendar/view/AbstractCalendar.js index 2b04ab61..2a87f090 100644 --- a/src/calendar/view/AbstractCalendar.js +++ b/src/calendar/view/AbstractCalendar.js @@ -1647,13 +1647,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) { @@ -1882,9 +1884,7 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { newRec = rec; } else { - newRec = rec.copy(null); - newRec.phantom = true; - newRec.set(Extensible.calendar.data.EventMappings.EventId.mapping, null); + newRec = rec.clone(); } if (me.fireEvent('beforeevent' + moveOrCopy, me, newRec, Ext.Date.clone(newStartDate)) !== false) { @@ -2031,7 +2031,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/data/Model.js b/src/data/Model.js index 11d9fa65..d8625dd4 100644 --- a/src/data/Model.js +++ b/src/data/Model.js @@ -85,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]; } From 2bfb62020722b22f7464de3a49cd267b7bd98ba6 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sun, 15 Sep 2013 14:38:42 +0200 Subject: [PATCH 05/29] Added missing exists() checks to language strings of recurrence widgets. --- src/locale/extensible-lang-de.js | 14 +++++++------- src/locale/extensible-lang-en_GB.js | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/locale/extensible-lang-de.js b/src/locale/extensible-lang-de.js index fe61a2a2..6f86dee8 100644 --- a/src/locale/extensible-lang-de.js +++ b/src/locale/extensible-lang-de.js @@ -276,13 +276,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 +290,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 +302,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 +320,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 +332,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 +340,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" diff --git a/src/locale/extensible-lang-en_GB.js b/src/locale/extensible-lang-en_GB.js index caecb686..f83e567e 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" From 86039106c7179a9dc288a1bc7c44b70a3a620b06 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sat, 15 Jun 2013 19:39:59 +0200 Subject: [PATCH 06/29] First version of new list view. --- build/resources/extensible.jsb2 | 12 + examples/calendar/TestApp/App.js | 14 +- examples/calendar/basic.js | 5 +- examples/calendar/custom-mappings.js | 2 + examples/calendar/data/Events.js | 2 +- examples/calendar/doc-types.js | 1 + examples/calendar/localization.js | 2 + examples/calendar/remote/php/app.php | 0 examples/calendar/remote/remote.js | 1 + examples/calendar/tabpanel.js | 1 + examples/calendar/window.js | 2 + resources/css/calendar.css | 42 ++++ src/calendar/CalendarPanel.js | 62 ++++- src/calendar/template/ListBody.js | 330 +++++++++++++++++++++++++ src/calendar/view/List.js | 297 +++++++++++++++++++++++ src/calendar/view/ListBody.js | 348 +++++++++++++++++++++++++++ src/calendar/view/ListHeader.js | 319 ++++++++++++++++++++++++ src/locale/extensible-lang-de.js | 32 +++ src/locale/extensible-lang-en.js | 33 +++ 19 files changed, 1500 insertions(+), 5 deletions(-) create mode 100644 examples/calendar/remote/php/app.php create mode 100644 src/calendar/template/ListBody.js create mode 100644 src/calendar/view/List.js create mode 100644 src/calendar/view/ListBody.js create mode 100644 src/calendar/view/ListHeader.js diff --git a/build/resources/extensible.jsb2 b/build/resources/extensible.jsb2 index 36777d8a..0cb50c05 100644 --- a/build/resources/extensible.jsb2 +++ b/build/resources/extensible.jsb2 @@ -38,6 +38,9 @@ },{ "text": "Month.js", "path": "../../src/calendar/template/" + },{ + "text": "ListBody.js", + "path": "../src/calendar/template/" },{ "text": "CalendarScrollManager.js", "path": "../../src/calendar/dd/" @@ -128,6 +131,15 @@ },{ "text": "MultiWeek.js", "path": "../../src/calendar/view/" + },{ + "text": "ListHeader.js", + "path": "../src/calendar/view/" + },{ + "text": "ListBody.js", + "path": "../src/calendar/view/" + },{ + "text": "List.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..63e5230e 100644 --- a/examples/calendar/TestApp/App.js +++ b/examples/calendar/TestApp/App.js @@ -119,7 +119,12 @@ Ext.define('Extensible.example.calendar.TestApp.App', { multiWeekViewCfg: { //weekCount: 3 }, - + + listViewCfg: { + linkDatesToDayView: true, + dateRangeDefault: '3months' + }, + // Some optional CalendarPanel configs to experiment with: //readOnly: true, //showDayView: false, @@ -127,6 +132,7 @@ Ext.define('Extensible.example.calendar.TestApp.App', { //showWeekView: false, //showMultiWeekView: false, //showMonthView: false, + showListView: true, //showNavBar: false, //showTodayText: false, //showTime: false, @@ -135,6 +141,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..fcd96757 100644 --- a/examples/calendar/basic.js +++ b/examples/calendar/basic.js @@ -26,7 +26,9 @@ Ext.onReady(function(){ renderTo: 'simple', title: 'Basic Calendar', width: 700, - height: 500 + height: 500, + activeItem: 3, // default to month view + showListView: true }); // @@ -38,6 +40,7 @@ Ext.onReady(function(){ eventStore: eventStore, renderTo: 'panel', title: 'Calendar with Panel Configs', + 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..4f3102ca 100644 --- a/examples/calendar/custom-mappings.js +++ b/examples/calendar/custom-mappings.js @@ -81,6 +81,8 @@ Ext.onReady(function(){ calendarStore: calendarStore, renderTo: 'cal', title: 'Custom Event Mappings', + showListView: true, + activeItem: 3, // default to month view width: 800, height: 700 }); diff --git a/examples/calendar/data/Events.js b/examples/calendar/data/Events.js index 6dc74aac..d389322b 100644 --- a/examples/calendar/data/Events.js +++ b/examples/calendar/data/Events.js @@ -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/doc-types.js b/examples/calendar/doc-types.js index e1cba78f..a1a71743 100644 --- a/examples/calendar/doc-types.js +++ b/examples/calendar/doc-types.js @@ -74,6 +74,7 @@ Ext.onReady(function(){ }), renderTo: 'cal', title: 'Doctype Tester', + showListView: true, activeItem: 1, width: 800, height: 700 diff --git a/examples/calendar/localization.js b/examples/calendar/localization.js index 8de026cd..515bc49f 100644 --- a/examples/calendar/localization.js +++ b/examples/calendar/localization.js @@ -107,6 +107,8 @@ Ext.onReady(function() { multiDayViewCfg: { dayCount: 5 }, + showListView: true, + 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/remote.js b/examples/calendar/remote/remote.js index 6fd78163..cb42c491 100644 --- a/examples/calendar/remote/remote.js +++ b/examples/calendar/remote/remote.js @@ -176,6 +176,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..5c00809d 100644 --- a/examples/calendar/tabpanel.js +++ b/examples/calendar/tabpanel.js @@ -33,6 +33,7 @@ Ext.onReady(function(){ width: 700, height: 500, activeItem: 1, + 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 diff --git a/examples/calendar/window.js b/examples/calendar/window.js index fa401ed0..cac59612 100644 --- a/examples/calendar/window.js +++ b/examples/calendar/window.js @@ -26,6 +26,8 @@ Ext.onReady(function(){ items: { // xtype is supported: xtype: 'extensible.calendarpanel', + activeItem: 3, // default to month view + showListView: true, eventStore: Ext.create('Extensible.calendar.data.MemoryEventStore', { // defined in ../data/Events.js data: Ext.create('Extensible.example.calendar.data.Events') diff --git a/resources/css/calendar.css b/resources/css/calendar.css index 0aabdc09..7ac682b5 100644 --- a/resources/css/calendar.css +++ b/resources/css/calendar.css @@ -754,6 +754,48 @@ td.ext-cal-dtitle-today div { display: none; } +/* ----------------------------------------- + * List view specific styles + */ +.ext-cal-list { + border-top: 1px solid #99BBE8; +} + +.ext-cal-list-hd { + border-bottom: 1px solid #99BBE8; +} + +.ext-cal-list-hd .x-panel-body { + background-color: #F0F4FA !important; +} +.ext-cal-list-hd .x-toolbar { + background-color: #F0F4FA !important; + background-image: none !important; +} + +.ext-cal-list-bd .ext-cal-evt-list td { + padding: 2px 10px; + font-size: 12px; + vertical-align: top; +} + +.ext-cal-list-bd .ext-cal-evt-list-details td { + padding: 1px 10px 1px 0; +} + +.ext-cal-list-bd .ext-cal-day-link { + cursor: pointer; +} + +.ext-cal-list-bd hr { + margin:10px 0; + color: #CCC; + background-color:#CCC; + height:1px; + border:0; +} + + /******************************************* * * Calendar navigation picker styles diff --git a/src/calendar/CalendarPanel.js b/src/calendar/CalendarPanel.js index 4b62830d..4cffcfda 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.List' ], /** @@ -58,6 +59,11 @@ 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} showListView + * True to include the list view (and toolbar button), false to hide them (defaults to false). + */ + showListView: false, /** * @cfg {Boolean} showNavBar * True to display the calendar navigation toolbar, false to hide it (defaults to true). Note that @@ -151,6 +157,11 @@ Ext.define('Extensible.calendar.CalendarPanel', { * Text to use for the 'Month' nav bar button. */ monthText: 'Month', + /** + * @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 +226,10 @@ 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} listViewCfg + * A config object that will be applied only to the {@link Extensible.calendar.view.List ListView} managed by this CalendarPanel. + */ /** * @cfg {Object} editViewCfg * A config object that will be applied only to the {@link Extensible.calendar.form.EventDetails @@ -301,7 +316,13 @@ Ext.define('Extensible.calendar.CalendarPanel', { this.viewCount++; this.showMonthView = true; } - + if(this.showListView){ + 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)); @@ -630,6 +651,33 @@ Ext.define('Extensible.calendar.CalendarPanel', { this.initEventRelay(month); this.add(month); } + if(this.showListView){ + var list = Ext.applyIf({ + xtype: 'extensible.listview', + title: this.listText, + listeners: { + 'dayclick': { + fn: function(vw, dt){ + this.showDay(dt); + }, + scope: this + }, + 'datechange': { + fn: function(){ + // ListView 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', @@ -899,6 +947,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); @@ -951,6 +1004,11 @@ Ext.define('Extensible.calendar.CalendarPanel', { onMonthNavClick: function() { this.setActiveView(this.id+'-month'); }, + + // private + onListNavClick: function(){ + this.setActiveView(this.id+'-list'); + }, /** * Return the calendar view that is currently active, which will be a subclass of diff --git a/src/calendar/template/ListBody.js b/src/calendar/template/ListBody.js new file mode 100644 index 00000000..0d547339 --- /dev/null +++ b/src/calendar/template/ListBody.js @@ -0,0 +1,330 @@ +/** + * @class Extensible.calendar.template.ListBody + * @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.ListBody ListBody}.

+ * + *

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.ListBody', { + extend: 'Ext.XTemplate', + + requires: [], + + /** + * @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-list-bd-prev-link', + /** + * @cfg {String} nextLinkSelector + * The class name applied to the previous link. + */ + nextLinkSelector: 'ext-cal-list-bd-next-link', + + + // private + constructor: function(config){ + + Ext.apply(this, config); + + Extensible.calendar.template.ListBody.superclass.constructor.call(this, + '', + '', + '', + '', + // '', + '', + '', // events is a MixedCollection + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '
{[Ext.Date.format(values.date, \"D M j\")]}', + '{[Ext.Date.format(values.date, this.dayDateFormat)]}', + '
{[this.getEventTimesMarkup(values, parent.date)]}', + '', + // Display a one-line summary of the event + '{[this.getTitleMarkup(values)]}', + '{[this.getReminderFlagMarkup(values)]}', + '{[this.getRecurrenceFlagMarkup(values)]}', + '', + '', + // Display event with all details + '{[this.getTitleMarkup(values)]}', + '{[this.getReminderFlagMarkup(values)]}', + '{[this.getRecurrenceFlagMarkup(values)]}', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '
', this.locationText, ':{[values.data[Extensible.calendar.data.EventMappings.Location.name]]}
', this.webLinkText, ':{[this.getWebLinkMarkup(values, true)]}
', this.notesText, ':{[this.getNotesMarkup(values)]}
', + '
', + '

', + this.noEventsText, + '
', + '
', this.prevLinkText, ' | ', this.nextLinkText, '


', + '
', + { + eventHasLocation: function(evt) { + var l = evt.data[Extensible.calendar.data.EventMappings.Location.name]; + return l && l != ""; + }, + eventHasLink: function(evt) { + var url = evt.data[Extensible.calendar.data.EventMappings.Url.name]; + return url && url != ""; + }, + eventHasNotes: function(evt) { + var n = evt.data[Extensible.calendar.data.EventMappings.Notes.name]; + return n && n != ""; + } + } + ); + }, + + // private + applyTemplate : function(o){ + if (Ext.getVersion().isLessThan('4.1')) { + return Extensible.calendar.template.ListBody.superclass.applyTemplate.call(this, o); + } + else { + return this.applyOut(o, []).join(''); + } + }, + + /** + * Returns event start and end times formatted for output. + * @param {Extensible.calendar.data.EventModel} evt + * @param {Date} dt The date for which to produce output. + * @return {String} + */ + getEventTimesMarkup: 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 the markup for the event title. + * @param {Extensible.calendar.data.EventModel} evt + * @return {String} + '{[this.getTitleMarkup(values)]}', + '{[values.data[Extensible.calendar.data.EventMappings.Location.name] != "" ? " - " + values.data[Extensible.calendar.data.EventMappings.Location.name] : ""]}', + */ + getTitleMarkup: function(evt) { + var result, + M = Extensible.calendar.data.EventMappings, + title = evt.data[M.Title.name]; + result = [ + '', + !title || title.length == 0 ? this.defaultEventTitleText : title, + '' + ]; + 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; + } + +}, +function() { + this.createAlias('apply', 'applyTemplate'); +}); \ No newline at end of file diff --git a/src/calendar/view/List.js b/src/calendar/view/List.js new file mode 100644 index 00000000..fecdae90 --- /dev/null +++ b/src/calendar/view/List.js @@ -0,0 +1,297 @@ +/** + * @class Extensible.calendar.view.List + * @extends Ext.Container + * + *

This class is currently beta code and the API is still subject to change before the next release.

+ * + *

List view display events as a sorted list, similar to the agenda view in Google calendar. It supports CRUD + * operations on events, filtering of events based on calendar and a selectable date range. The view can be + * switched between details view and summary view.

+ * + *

The view is divided into two main sections: the {@link Extensible.calendar.view.ListHeader header} and the + * {@link Extensible.calendar.view.ListBody 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.ListHeader ListHeader} and a {@link Extensible.calendar.view.ListBody ListBody}. As such + * this class accepts any config values that are valid for ListHeaderView and ListBodyView 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.

+ * + * @author Gabriel Sidler, sidler@teamup.com + * @constructor + * @param {Object} config The config object + */ +Ext.define('Extensible.calendar.view.List', { + extend: 'Ext.Container', + alias: 'widget.extensible.listview', + + requires: [ + 'Extensible.calendar.view.AbstractCalendar', + 'Extensible.calendar.view.ListHeader', + 'Extensible.calendar.view.ListBody' + ], + + /** + * @cfg {String} hideMode + *

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.

+ */ + hideMode: 'offsets', + + /** + * @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 + initComponent : function(){ + + // Pass on initial configuration to sub-components + var cfg = Ext.apply({}, this.initialConfig); + + var header = Ext.applyIf({ + xtype: 'extensible.listheaderview', + id: 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.listbodyview', + id: this.id+'-bd', + ownerCalendarPanel: this.ownerCalendarPanel, + ownerCalendarView: this + }, cfg); + + this.items = [header, body]; + this.addCls('ext-cal-list 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 (ListView)'); + // 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] = false; + + 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-list-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:
    + *
  • start Date :
    The start date of the view
  • + *
  • end Date :
    The end date of the view
+ * For example:

+     var bounds = view.getViewBounds();
+     alert('Start: '+bounds.start);
+     alert('End: '+bounds.end);
+     
+ * @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/ListBody.js b/src/calendar/view/ListBody.js new file mode 100644 index 00000000..2d71b9c1 --- /dev/null +++ b/src/calendar/view/ListBody.js @@ -0,0 +1,348 @@ +/** + * @class Extensible.calendar.view.ListBody + * @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 list view. Normally you should not need to use this class directly + * -- instead you should use {@link Extensible.calendar.view.List List} view which aggregates this class and the + * {@link Extensible.calendar.view.ListHeader ListHeader} 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.ListBody}.

+ * + * @author Gabriel Sidler, sidler@teamup.com + * @constructor + * @param {Object} config The config object + */ +Ext.define('Extensible.calendar.view.ListBody', { + extend: 'Extensible.calendar.view.AbstractCalendar', + alias: 'widget.extensible.listbodyview', + + requires: [ + 'Ext.XTemplate', + 'Extensible.calendar.template.ListBody' + ], + + /** + * @cfg {Boolean} linkDatesToDayView + * True to link dates to the {@link Extensible.calendar.view.Day day view}. + */ + linkDatesToDayView: true, + + /** + * @property filterConfig + * @type {Object} + * A reference to an object that contains key/value pairs to be used as the filtering configuration when loading events. + * Typically, such an object is obtained from method {@link Ext.form.Basic#getFieldValues} and assigned to instances of + * this class using method {@link #setFilterConfig}. + */ + filterConfig: { + period: 'month', + details: false + }, + + /** + * @property ownerCalendarView + * @type {Ext.Container} + * A reference to the calendar view that hosts this view. Read-only. + */ + + // private properties -- do not override + dayLinkSelector: '.ext-cal-day-link', + dayLinkIdDelimiter: 'ext-cal-day-', + prevLinkSelector: 'ext-cal-list-bd-prev-link', + nextLinkSelector: 'ext-cal-list-bd-next-link', + flex: 1, + autoScroll: true, + padding: '10 0 10 0', + + // private + initComponent : function(){ + this.callParent(arguments); + + this.addEvents({ + /** + * @event dayclick + * Fires after the user clicks on a day date + * @param {Extensible.calendar.view.ListBody} this + * @param {Date} dt The date that was clicked on. + */ + dayclick: true + }); + }, + + // Private + renderTemplate : function(){ + var templateParams = this.getTemplateParams(); + + templateParams.days = this.getTemplateEventData(); + 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. The returned value is an array of days containing an array of events. + */ + getTemplateEventData : function(){ + var M = Extensible.calendar.data.EventMappings, + calM = Extensible.calendar.data.CalendarMappings, + events, + days = {}, + daysArray = [], + startDtView, endDtView, + extraClasses, + colorCls = 'x-cal-default', + Dt = Extensible.Date, + viewBounds; + + events = this.store.queryBy(function(rec){ + return this.isEventVisible(rec.data); + }, this); + + // Get start and end date of view period + viewBounds = this.getViewBounds(); + startDtView = viewBounds.start; + endDtView = viewBounds.end; + + // Loop over all events of period and generate list items. Single-day events produce one item per event. + // Multi-day events produce multiple items, one for each day. + 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; + }, + + // private + afterRender : function(){ + if(!this.tpl){ + this.tpl = Ext.create('Extensible.calendar.template.ListBody', { + id: this.id, + linkDatesToDayView: this.linkDatesToDayView, + defaultEventTitleText: this.defaultEventTitleText, + prevLinkSelector: this.prevLinkSelector, + nextLinkSelector: this.nextLinkSelector + }); + this.tpl.compile(); + } + this.addCls('ext-cal-list-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 (ListView)'); + 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 filtering conditions. + */ + setFilterConfig: function(filterConfig) { + this.filterConfig = filterConfig; + this.tpl.showEventDetails = this.filterConfig.details ? true: false; + }, + + /** + * Little 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, + filterConfig = me.filterConfig || {period: 'month'}, + period = filterConfig.period || 'month', + 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, + filterConfig = me.filterConfig || {period: 'month'}, + period = filterConfig.period || 'month', + 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, + filterConfig = me.filterConfig || {period: 'month'}, + period = filterConfig.period || 'month', + 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.ListBody.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); + } + + } +}); \ No newline at end of file diff --git a/src/calendar/view/ListHeader.js b/src/calendar/view/ListHeader.js new file mode 100644 index 00000000..610ea2ec --- /dev/null +++ b/src/calendar/view/ListHeader.js @@ -0,0 +1,319 @@ +/** + * @class Extensible.calendar.view.ListHeader + * @extends Ext.panel.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.List List} view. Normally you should + * not need to use this class directly -- instead you should use {@link Extensible.calendar.view.List List} view which + * aggregates this class and the {@link Extensible.calendar.view.ListBody ListBody} 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.

+ * + * @author Gabriel Sidler, sidler@teamup.com + * @constructor + * @param {Object} config The config object + */ +Ext.define('Extensible.calendar.view.ListHeader', { + extend: 'Ext.form.Panel', + alias: 'widget.extensible.listheaderview', + + requires: [ + 'Ext.form.ComboBox', + 'Ext.Button', + 'Ext.data.Store', + 'Ext.tip.QuickTipManager' + ], + + /** + * @property ownerCalendarView + * @type Ext.Container + * A reference to the calendar view that hosts this view. Read-only. + */ + + /** + * @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} 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 {Boolean} showDetailsDefault + * Defines the default value for the checkbox to show details. Defaults to false. + */ + showDetailsDefault: false, + + // private configs + preventHeader: true, + autoHeight: true, + border: 0, + defaults: { + labelWidth: 100 + }, + + + // private + initComponent : function(){ + var tbItems = this.getToolbarConfig(); + + this.dateRangeOptions = this.getDateRangeOptions(); + + 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-list-hd-tb', + items: tbItems + }]; + } + + if (this.items.length == 0 && tbItems.length == 0) { + this.style = {borderBottom: '0px'}; + } + + this.callParent(arguments); + + this.addEvents({ + /** + * @event formchange + * Fires after the filter form changes. + * @param {Extensible.calendar.view.ListHeader} this + * @param {Ext.form.Basic} form The filter form. + * @param {Ext.form.field.Field} field Form field that changed. + * @param {Object} newValue New form field value. + * @param {Object} oldValue Old form field value. + * @param {Object} eOpts The options object passed to {@link Ext.util.Observable.addListener}. + */ + formchange: true, + + /** + * @event addevent + * Fires after the user clicks the add event button. + * @param {Extensible.calendar.view.ListHeader} this + * @param {Ext.button.Button} button The button clicked. + * @param {Event} event + * @param {Object} eOpts The options object passed to {@link Ext.util.Observable.addListener}. + */ + addevent: true + }); + + }, + + /** + *

This function is called by this form panel to obtain the definition of form fields. Override this function to + * modify the form fields displayed by this panel.

+ * @return {Array} An array of Object + */ + getFormConfig: function() { + return [{ + xtype: 'combo', + id: this.id+'-daterange', + mode: 'local', + value: this.dateRangeDefault, + triggerAction: 'all', + forceSelection: true, + editable: false, + width: 220, + 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} 
', + listeners: { + change: {fn: this.onFormChange, scope: this} + } + },{ + xtype: 'checkboxfield', + id: this.id+'-showdetails', + value: this.showDetailsDefault, + inputvalue: '1', + fieldLabel: this.showDetailsText, + name: 'details', + listeners: { + change: {fn: this.onFormChange, scope: this} + } + }]; + }, + + /** + *

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.

+ * @return {Array} An array of Object. + */ + getToolbarConfig: function() { + var result = []; + if (this.readOnly !== true) { + result.push({ + text : this.addBtnText, + listeners: { + click: { + fn: this.onAddEvent, + scope: this + } + } + }); + } + result.push( + '->', + { + text : this.resetBtnText, + handler: function() { + this.up('form').getForm().reset(); + } + } + ); + return result; + }, + + /** + *

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:


+[
+     {name : 'One Day',   value: 'day'},
+     {name : 'One Week',  value: 'week'},
+     {name : 'One Month',  value: 'month'},
+     {name : '3 Months',  value: '3months'},
+     {name : 'One Year', value: 'year'}
+]
+     

+ * @return {Object} + */ + getDateRangeOptions: function() { + return [ + {name : this.dateRangeOneDay, value: 'day'}, + {name : this.dateRangeOneWeek, value: 'week'}, + {name : this.dateRangeOneMonth, value: 'month'}, + {name : this.dateRangeThreeMonths, value: '3months'}, + {name : this.dateRangeOneYear, value: 'year'} + ]; + }, + + /* 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); + }, + + /* Private + * Event handler that is called when the user clicks on the add event button. + * @param {Extensible.calendar.view.ListHeader} 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.addCls('ext-cal-list-hd'); + this.callParent(arguments); + }, + + // private + refresh : function(reloadData){ + Extensible.log('refresh (ListHeader)'); + this.callParent(arguments); + }, + + /** + * This method is called by the {@link Extensible.calendar.view.List List} 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){ + } + +}); \ No newline at end of file diff --git a/src/locale/extensible-lang-de.js b/src/locale/extensible-lang-de.js index fe61a2a2..42fb0bb6 100644 --- a/src/locale/extensible-lang-de.js +++ b/src/locale/extensible-lang-de.js @@ -40,6 +40,7 @@ Ext.onReady(function() { dayText: 'Tag', weekText: 'Woche', monthText: 'Monat', + listText: 'Liste', jumpToText: 'Springe zu:', goText: 'Los', multiDayText: '{0} Tage', @@ -359,6 +360,37 @@ Ext.onReady(function() { }); } + /* + * Strings for agenda view, added in x.x.x + */ + if (exists('Extensible.calendar.template.ListBody')) { + Ext.apply(Extensible.calendar.template.ListBody.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.ListHeader')) { + Ext.apply(Extensible.calendar.view.ListHeader.prototype, { + dateRangeOneDay: 'Ein Tag', + dateRangeOneWeek: 'Eine Woche', + dateRangeOneMonth: 'Ein Monat', + dateRangeThreeMonths: 'Drei Monate', + dateRangeOneYear: 'Ein Jahr', + dateRangeText: 'Datumsbereich', + 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..17ecc2a3 100644 --- a/src/locale/extensible-lang-en.js +++ b/src/locale/extensible-lang-en.js @@ -46,6 +46,7 @@ Ext.onReady(function() { dayText: 'Day', weekText: 'Week', monthText: 'Month', + listText: 'List', jumpToText: 'Jump to:', goText: 'Go', multiDayText: '{0} Days', // deprecated @@ -362,6 +363,38 @@ Ext.onReady(function() { nthWeekdayDateFormat: 'S' // displays the ordinal postfix, e.g. th for 5th. } + + /* + * Strings for agenda view. Added in x.x.x + */ + + if (exists('Extensible.calendar.template.ListBody')) { + Ext.apply(Extensible.calendar.template.ListBody.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.ListHeader')) { + Ext.apply(Extensible.calendar.view.ListHeader.prototype, { + dateRangeOneDay: 'One day', + dateRangeOneWeek: 'One week', + dateRangeOneMonth: 'One month', + dateRangeThreeMonths: 'Three months', + dateRangeOneYear: 'One year', + dateRangeText: 'Date range', + showDetailsText: 'Show details', + addBtnText: 'Add event', + resetBtnText: 'Reset' }); } From 36a2dd90e46c4c74fdbf2288c2053fe0bc1992b7 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sat, 15 Jun 2013 19:40:35 +0200 Subject: [PATCH 07/29] Simplified configuration of default date range for list view. In the past it was necessary to configure the default date range separately for ListHeader and ListBody. Now, the default date range can be configured in one place in class List. --- src/calendar/view/ListBody.js | 46 +++++++++++++++++++-------------- src/calendar/view/ListHeader.js | 2 +- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/calendar/view/ListBody.js b/src/calendar/view/ListBody.js index 2d71b9c1..30f833e4 100644 --- a/src/calendar/view/ListBody.js +++ b/src/calendar/view/ListBody.js @@ -32,16 +32,11 @@ Ext.define('Extensible.calendar.view.ListBody', { linkDatesToDayView: true, /** - * @property filterConfig - * @type {Object} - * A reference to an object that contains key/value pairs to be used as the filtering configuration when loading events. - * Typically, such an object is obtained from method {@link Ext.form.Basic#getFieldValues} and assigned to instances of - * this class using method {@link #setFilterConfig}. - */ - filterConfig: { - period: 'month', - details: false - }, + * @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', /** * @property ownerCalendarView @@ -49,7 +44,15 @@ Ext.define('Extensible.calendar.view.ListBody', { * A reference to the calendar view that hosts this view. Read-only. */ - // private properties -- do not override + // 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-list-bd-prev-link', @@ -60,7 +63,11 @@ Ext.define('Extensible.calendar.view.ListBody', { // private initComponent : function(){ - this.callParent(arguments); + + this.filterConfig = { + period: this.dateRangeDefault, + details: false + }; this.addEvents({ /** @@ -71,6 +78,8 @@ Ext.define('Extensible.calendar.view.ListBody', { */ dayclick: true }); + + this.callParent(arguments); }, // Private @@ -224,7 +233,7 @@ Ext.define('Extensible.calendar.view.ListBody', { /** * Sets the filter configuration to be used when calculating view bounds and loading events. - * @param {Object} filterConfig An object of key/value pairs representing filtering conditions. + * @param {Object} filterConfig An object of key/value pairs representing filter conditions. */ setFilterConfig: function(filterConfig) { this.filterConfig = filterConfig; @@ -232,7 +241,7 @@ Ext.define('Extensible.calendar.view.ListBody', { }, /** - * Little helper function that converts a string expressing a date period into an object that can be used as + * 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. @@ -260,8 +269,7 @@ Ext.define('Extensible.calendar.view.ListBody', { var me = this, Dt = Extensible.Date, start = startDate || me.startDate, - filterConfig = me.filterConfig || {period: 'month'}, - period = filterConfig.period || 'month', + period = me.filterConfig.period || this.dateRangeDefault, addParam; addParam = this.getDateAddParam(period, false); @@ -290,8 +298,7 @@ Ext.define('Extensible.calendar.view.ListBody', { */ moveNext : function(/*private*/reload){ var me = this, - filterConfig = me.filterConfig || {period: 'month'}, - period = filterConfig.period || 'month', + period = me.filterConfig.period || this.dateRangeDefault, addParam; addParam = this.getDateAddParam(period); return this.moveTo(Extensible.Date.add(this.viewStart, addParam), reload); @@ -303,8 +310,7 @@ Ext.define('Extensible.calendar.view.ListBody', { */ movePrev : function(/*private*/reload){ var me = this, - filterConfig = me.filterConfig || {period: 'month'}, - period = filterConfig.period || 'month', + period = me.filterConfig.period || this.dateRangeDefault, addParam; addParam = this.getDateAddParam(period, true); return this.moveTo(Extensible.Date.add(this.viewStart, addParam), reload); diff --git a/src/calendar/view/ListHeader.js b/src/calendar/view/ListHeader.js index 610ea2ec..196fb32c 100644 --- a/src/calendar/view/ListHeader.js +++ b/src/calendar/view/ListHeader.js @@ -1,6 +1,6 @@ /** * @class Extensible.calendar.view.ListHeader - * @extends Ext.panel.Panel + * @extends Ext.form.Panel * *

This class is currently beta code and the API is still subject to change before the next release.

* From 97cc175fc06189498ab2daa8e6c7ca7faa716ce5 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sat, 15 Jun 2013 19:41:26 +0200 Subject: [PATCH 08/29] Renamed list view to agenda view to be more in line with other widly used calendars. --- Extensible-config-orig.js | 208 ++++++++++++++++++ build/resources/extensible.jsb2 | 8 +- examples/calendar/TestApp/App.js | 4 +- examples/calendar/basic.js | 4 +- examples/calendar/custom-mappings.js | 2 +- examples/calendar/custom-views.html | 2 +- examples/calendar/doc-types.js | 2 +- examples/calendar/localization.js | 2 +- examples/calendar/remote/remote.js | 3 +- examples/calendar/tabpanel.js | 2 +- examples/calendar/window.js | 2 +- resources/css/calendar.css | 18 +- src/calendar/CalendarPanel.js | 44 ++-- .../template/{ListBody.js => AgendaBody.js} | 18 +- src/calendar/view/{List.js => Agenda.js} | 32 +-- .../view/{ListBody.js => AgendaBody.js} | 30 +-- .../view/{ListHeader.js => AgendaHeader.js} | 26 +-- src/locale/extensible-lang-de.js | 10 +- src/locale/extensible-lang-en.js | 11 +- 19 files changed, 318 insertions(+), 110 deletions(-) create mode 100644 Extensible-config-orig.js rename src/calendar/template/{ListBody.js => AgendaBody.js} (95%) rename src/calendar/view/{List.js => Agenda.js} (90%) rename src/calendar/view/{ListBody.js => AgendaBody.js} (93%) rename src/calendar/view/{ListHeader.js => AgendaHeader.js} (93%) diff --git a/Extensible-config-orig.js b/Extensible-config-orig.js new file mode 100644 index 00000000..2135b54d --- /dev/null +++ b/Extensible-config-orig.js @@ -0,0 +1,208 @@ +Extensible = { + version: '1.5.1' +}; +/** + * 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 the proper paths directly. + */ +Extensible.Config = { + /** + * Edit the values of these default configs to customize how Ext JS and Extensible are loaded. + */ + 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. + * + * 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.0.7 from the Sencha CDN via + * 'http://cdn.sencha.io/ext-4.0.7-gpl/'). Path should be absolute and should end with a '/'. + * + * Note that the Sencha CDN does not always provide the most current version of Ext JS + * available (for example, support subscribers often have access to more up-to-date builds). + * If the version you need is not hosted you'll have to download it locally and update this + * path accordingly. + * + * Alternate example values: + * + * // Older Ext JS versions: + * http://cdn.sencha.io/ext-4.0.2/ + * + * // Direct to cachefly.net, e.g. if sencha.io is down: + * http://extjs.cachefly.net/ext-4.0.7-gpl/ + * + * // A custom absolute path: + * http://localhost/extjs/ + * http://mydomain/extjs/4.0.2/ + * + * @config {String} extJsRoot + */ + extJsRoot: 'http://cdn.sencha.io/ext-4.0.7-gpl/', + + /** + * 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 + * to the url to enforce reloading Extensible files with each page refresh (useful if you are + * 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. + * + * 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 + }, + + /** + * 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.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) { + // If hosted at ext.ensible.com force non-debug release build includes + 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)), + suffix = '', + bootstrap = ''; + + switch (me.mode) { + case 'debug': + suffix = '-all-debug'; + break; + + case 'release': + suffix = '-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) { + suffix = '-all-debug'; + } + else { + bootstrap = '-bootstrap'; + } + } + + 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' + suffix + '.js'); + me.includeScript(me.extensibleRoot + 'lib/extensible' + suffix + bootstrap + '.js' + cacheBuster); + me.includeScript(me.extensibleRoot + 'examples/examples.js?_dc=' + Extensible.version); + } +}; + +/* + * Kick it off. To customize the configuration settings from external code, you can create a global + * object -- before including this Extensible-config.js script -- called "ExtensibleDefaults" and give + * it properties corresponding to the Extensible.Config configs you want to set. If it exists the + * ExtensibleDefaults object will be used and then destroyed automatically, otherwise the Extensible.Config + * defaults will be used. Any options not specified in the ExtensibleDefaults object will simply use the + * default value. For example: + * + * ExtensibleDefaults = { + * mode: 'dynamic' + * } + * + * Note that this global config object is primarily supported for testability and for one-off + * overriding of Extensible.Config. To change the configuration that you plan to use for your normal + * day-to-day use you should simply edit the Extensible.Config.defaults as needed to change the + * settings globally without having to create this object on each page. + */ +Extensible.Config.init(); + +// Clean up the global config override if it exists +try { + delete window.ExtensibleDefaults; +} +catch(ex) { + window.ExtensibleDefaults = null; +} diff --git a/build/resources/extensible.jsb2 b/build/resources/extensible.jsb2 index 0cb50c05..c9cd9eb8 100644 --- a/build/resources/extensible.jsb2 +++ b/build/resources/extensible.jsb2 @@ -39,7 +39,7 @@ "text": "Month.js", "path": "../../src/calendar/template/" },{ - "text": "ListBody.js", + "text": "AgendaBody.js", "path": "../src/calendar/template/" },{ "text": "CalendarScrollManager.js", @@ -132,13 +132,13 @@ "text": "MultiWeek.js", "path": "../../src/calendar/view/" },{ - "text": "ListHeader.js", + "text": "AgendaHeader.js", "path": "../src/calendar/view/" },{ - "text": "ListBody.js", + "text": "AgendaBody.js", "path": "../src/calendar/view/" },{ - "text": "List.js", + "text": "Agenda.js", "path": "../src/calendar/view/" },{ "text": "CalendarPanel.js", diff --git a/examples/calendar/TestApp/App.js b/examples/calendar/TestApp/App.js index 63e5230e..38cbc089 100644 --- a/examples/calendar/TestApp/App.js +++ b/examples/calendar/TestApp/App.js @@ -120,7 +120,7 @@ Ext.define('Extensible.example.calendar.TestApp.App', { //weekCount: 3 }, - listViewCfg: { + agendaViewCfg: { linkDatesToDayView: true, dateRangeDefault: '3months' }, @@ -132,7 +132,7 @@ Ext.define('Extensible.example.calendar.TestApp.App', { //showWeekView: false, //showMultiWeekView: false, //showMonthView: false, - showListView: true, + showAgendaView: true, //showNavBar: false, //showTodayText: false, //showTime: false, diff --git a/examples/calendar/basic.js b/examples/calendar/basic.js index fcd96757..c36e4cfa 100644 --- a/examples/calendar/basic.js +++ b/examples/calendar/basic.js @@ -28,7 +28,7 @@ Ext.onReady(function(){ width: 700, height: 500, activeItem: 3, // default to month view - showListView: true + showAgendaView: true }); // @@ -40,7 +40,7 @@ Ext.onReady(function(){ eventStore: eventStore, renderTo: 'panel', title: 'Calendar with Panel Configs', - showListView: true, + showAgendaView: 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 4f3102ca..dcd016a4 100644 --- a/examples/calendar/custom-mappings.js +++ b/examples/calendar/custom-mappings.js @@ -81,7 +81,7 @@ Ext.onReady(function(){ calendarStore: calendarStore, renderTo: 'cal', title: 'Custom Event Mappings', - showListView: true, + showAgendaView: 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/doc-types.js b/examples/calendar/doc-types.js index a1a71743..879968a2 100644 --- a/examples/calendar/doc-types.js +++ b/examples/calendar/doc-types.js @@ -74,7 +74,7 @@ Ext.onReady(function(){ }), renderTo: 'cal', title: 'Doctype Tester', - showListView: true, + showAgendaView: true, activeItem: 1, width: 800, height: 700 diff --git a/examples/calendar/localization.js b/examples/calendar/localization.js index 515bc49f..1394e15b 100644 --- a/examples/calendar/localization.js +++ b/examples/calendar/localization.js @@ -107,7 +107,7 @@ Ext.onReady(function() { multiDayViewCfg: { dayCount: 5 }, - showListView: true, + showAgendaView: true, activeItem: 4, // default to month view eventStore: Ext.create('Extensible.calendar.data.MemoryEventStore', { // defined in ../data/Events.js diff --git a/examples/calendar/remote/remote.js b/examples/calendar/remote/remote.js index cb42c491..3a0505c1 100644 --- a/examples/calendar/remote/remote.js +++ b/examples/calendar/remote/remote.js @@ -164,7 +164,8 @@ 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 }); Ext.create('Ext.container.Viewport', { diff --git a/examples/calendar/tabpanel.js b/examples/calendar/tabpanel.js index 5c00809d..9794667e 100644 --- a/examples/calendar/tabpanel.js +++ b/examples/calendar/tabpanel.js @@ -33,7 +33,7 @@ Ext.onReady(function(){ width: 700, height: 500, activeItem: 1, - showListView: true, + showAgendaView: 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 diff --git a/examples/calendar/window.js b/examples/calendar/window.js index cac59612..a6156ef4 100644 --- a/examples/calendar/window.js +++ b/examples/calendar/window.js @@ -27,7 +27,7 @@ Ext.onReady(function(){ // xtype is supported: xtype: 'extensible.calendarpanel', activeItem: 3, // default to month view - showListView: true, + showAgendaView: true, eventStore: Ext.create('Extensible.calendar.data.MemoryEventStore', { // defined in ../data/Events.js data: Ext.create('Extensible.example.calendar.data.Events') diff --git a/resources/css/calendar.css b/resources/css/calendar.css index 7ac682b5..e14474a5 100644 --- a/resources/css/calendar.css +++ b/resources/css/calendar.css @@ -755,39 +755,39 @@ td.ext-cal-dtitle-today div { } /* ----------------------------------------- - * List view specific styles + * Agenda view specific styles */ -.ext-cal-list { +.ext-cal-agenda { border-top: 1px solid #99BBE8; } -.ext-cal-list-hd { +.ext-cal-agenda-hd { border-bottom: 1px solid #99BBE8; } -.ext-cal-list-hd .x-panel-body { +.ext-cal-agenda-hd .x-panel-body { background-color: #F0F4FA !important; } -.ext-cal-list-hd .x-toolbar { +.ext-cal-agenda-hd .x-toolbar { background-color: #F0F4FA !important; background-image: none !important; } -.ext-cal-list-bd .ext-cal-evt-list td { +.ext-cal-agenda-bd .ext-cal-evt-agenda td { padding: 2px 10px; font-size: 12px; vertical-align: top; } -.ext-cal-list-bd .ext-cal-evt-list-details td { +.ext-cal-agenda-bd .ext-cal-evt-agenda-details td { padding: 1px 10px 1px 0; } -.ext-cal-list-bd .ext-cal-day-link { +.ext-cal-agenda-bd .ext-cal-day-link { cursor: pointer; } -.ext-cal-list-bd hr { +.ext-cal-agenda-bd hr { margin:10px 0; color: #CCC; background-color:#CCC; diff --git a/src/calendar/CalendarPanel.js b/src/calendar/CalendarPanel.js index 4cffcfda..9c7741a3 100644 --- a/src/calendar/CalendarPanel.js +++ b/src/calendar/CalendarPanel.js @@ -15,7 +15,7 @@ Ext.define('Extensible.calendar.CalendarPanel', { 'Extensible.calendar.view.Month', 'Extensible.calendar.view.MultiDay', 'Extensible.calendar.view.MultiWeek', - 'Extensible.calendar.view.List' + 'Extensible.calendar.view.Agenda' ], /** @@ -60,10 +60,10 @@ Ext.define('Extensible.calendar.CalendarPanel', { */ showMonthView: true, /** - * @cfg {Boolean} showListView - * True to include the list view (and toolbar button), false to hide them (defaults to false). + * @cfg {Boolean} showAgendaView + * True to include the agenda view (and toolbar button), false to hide them (defaults to false). */ - showListView: false, + showAgendaView: false, /** * @cfg {Boolean} showNavBar * True to display the calendar navigation toolbar, false to hide it (defaults to true). Note that @@ -158,10 +158,10 @@ Ext.define('Extensible.calendar.CalendarPanel', { */ monthText: 'Month', /** - * @cfg {String} listText - * Text to use for the 'List' nav bar button. + * @cfg {String} agendaText + * Text to use for the 'Agenda' nav bar button. */ - listText: 'List', + agendaText: 'Agenda', /** * @cfg {Boolean} editModal * True to show the default event editor window modally over the entire page, false to allow user @@ -227,8 +227,8 @@ Ext.define('Extensible.calendar.CalendarPanel', { * managed by this CalendarPanel. */ /** - * @cfg {Object} listViewCfg - * A config object that will be applied only to the {@link Extensible.calendar.view.List ListView} managed by this CalendarPanel. + * @cfg {Object} agendaViewCfg + * A config object that will be applied only to the {@link Extensible.calendar.view.Agenda AgendaView} managed by this CalendarPanel. */ /** * @cfg {Object} editViewCfg @@ -316,9 +316,9 @@ Ext.define('Extensible.calendar.CalendarPanel', { this.viewCount++; this.showMonthView = true; } - if(this.showListView){ + if(this.showAgendaView){ this.tbar.items.push({ - id: this.id+'-tb-list', text: this.listText, handler: this.onListNavClick, scope: this, toggleGroup: this.id+'-tb-views' + id: this.id+'-tb-agenda', text: this.agendaText, handler: this.onAgendaNavClick, scope: this, toggleGroup: this.id+'-tb-views' }); this.viewCount++; } @@ -651,10 +651,10 @@ Ext.define('Extensible.calendar.CalendarPanel', { this.initEventRelay(month); this.add(month); } - if(this.showListView){ - var list = Ext.applyIf({ - xtype: 'extensible.listview', - title: this.listText, + if(this.showAgendaView){ + var agenda = Ext.applyIf({ + xtype: 'extensible.agendaview', + title: this.agendaText, listeners: { 'dayclick': { fn: function(vw, dt){ @@ -664,7 +664,7 @@ Ext.define('Extensible.calendar.CalendarPanel', { }, 'datechange': { fn: function(){ - // ListView allows the changing of start and end dates from within in the view. Update + // AgendaView allows the changing of start and end dates from within in the view. Update // the nav state this happens. this.updateNavState(); }, @@ -673,10 +673,10 @@ Ext.define('Extensible.calendar.CalendarPanel', { } }, sharedViewCfg); - list = Ext.apply(Ext.apply(list, this.viewConfig), this.listViewCfg); - list.id = this.id+'-list'; - this.initEventRelay(list); - this.add(list); + agenda = Ext.apply(Ext.apply(agenda, this.viewConfig), this.agendaViewCfg); + agenda.id = this.id+'-agenda'; + this.initEventRelay(agenda); + this.add(agenda); } this.add(Ext.applyIf({ @@ -1006,8 +1006,8 @@ Ext.define('Extensible.calendar.CalendarPanel', { }, // private - onListNavClick: function(){ - this.setActiveView(this.id+'-list'); + onAgendaNavClick: function(){ + this.setActiveView(this.id+'-agenda'); }, /** diff --git a/src/calendar/template/ListBody.js b/src/calendar/template/AgendaBody.js similarity index 95% rename from src/calendar/template/ListBody.js rename to src/calendar/template/AgendaBody.js index 0d547339..31fbc98b 100644 --- a/src/calendar/template/ListBody.js +++ b/src/calendar/template/AgendaBody.js @@ -1,10 +1,10 @@ /** - * @class Extensible.calendar.template.ListBody + * @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.ListBody ListBody}.

+ *

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}.

@@ -13,7 +13,7 @@ * @constructor * @param {Object} config The config object */ -Ext.define('Extensible.calendar.template.ListBody', { +Ext.define('Extensible.calendar.template.AgendaBody', { extend: 'Ext.XTemplate', requires: [], @@ -92,12 +92,12 @@ Ext.define('Extensible.calendar.template.ListBody', { * @cfg {String} prevLinkSelector * The class name applied to the previous link. */ - prevLinkSelector: 'ext-cal-list-bd-prev-link', + prevLinkSelector: 'ext-cal-agenda-bd-prev-link', /** * @cfg {String} nextLinkSelector * The class name applied to the previous link. */ - nextLinkSelector: 'ext-cal-list-bd-next-link', + nextLinkSelector: 'ext-cal-agenda-bd-next-link', // private @@ -105,8 +105,8 @@ Ext.define('Extensible.calendar.template.ListBody', { Ext.apply(this, config); - Extensible.calendar.template.ListBody.superclass.constructor.call(this, - '', + Extensible.calendar.template.AgendaBody.superclass.constructor.call(this, + '
', '', '', '', @@ -133,7 +133,7 @@ Ext.define('Extensible.calendar.template.ListBody', { '{[this.getTitleMarkup(values)]}', '{[this.getReminderFlagMarkup(values)]}', '{[this.getRecurrenceFlagMarkup(values)]}', - '
', + '
', '', '', '', @@ -191,7 +191,7 @@ Ext.define('Extensible.calendar.template.ListBody', { // private applyTemplate : function(o){ if (Ext.getVersion().isLessThan('4.1')) { - return Extensible.calendar.template.ListBody.superclass.applyTemplate.call(this, o); + return Extensible.calendar.template.AgendaBody.superclass.applyTemplate.call(this, o); } else { return this.applyOut(o, []).join(''); diff --git a/src/calendar/view/List.js b/src/calendar/view/Agenda.js similarity index 90% rename from src/calendar/view/List.js rename to src/calendar/view/Agenda.js index fecdae90..893332ea 100644 --- a/src/calendar/view/List.js +++ b/src/calendar/view/Agenda.js @@ -1,22 +1,22 @@ /** - * @class Extensible.calendar.view.List + * @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.

* - *

List view display events as a sorted list, similar to the agenda view in Google calendar. It supports CRUD + *

Agenda view display events as a sorted list, similar to the agenda view in Google calendar. It supports CRUD * operations on events, filtering of events based on calendar and a selectable date range. The view can be - * switched between details view and summary view.

+ * switched between a summary view and a details view.

* - *

The view is divided into two main sections: the {@link Extensible.calendar.view.ListHeader header} and the - * {@link Extensible.calendar.view.ListBody event list}. The header hosts a form and a toolbar that can be + *

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.ListHeader ListHeader} and a {@link Extensible.calendar.view.ListBody ListBody}. As such - * this class accepts any config values that are valid for ListHeaderView and ListBodyView and passes those through + * 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.

* @@ -24,14 +24,14 @@ * @constructor * @param {Object} config The config object */ -Ext.define('Extensible.calendar.view.List', { +Ext.define('Extensible.calendar.view.Agenda', { extend: 'Ext.Container', - alias: 'widget.extensible.listview', + alias: 'widget.extensible.agendaview', requires: [ 'Extensible.calendar.view.AbstractCalendar', - 'Extensible.calendar.view.ListHeader', - 'Extensible.calendar.view.ListBody' + 'Extensible.calendar.view.AgendaHeader', + 'Extensible.calendar.view.AgendaBody' ], /** @@ -64,7 +64,7 @@ Ext.define('Extensible.calendar.view.List', { var cfg = Ext.apply({}, this.initialConfig); var header = Ext.applyIf({ - xtype: 'extensible.listheaderview', + xtype: 'extensible.agendaheaderview', id: this.id+'-hd', ownerCalendarView: this, listeners: { @@ -74,14 +74,14 @@ Ext.define('Extensible.calendar.view.List', { }, cfg); var body = Ext.applyIf({ - xtype: 'extensible.listbodyview', + xtype: 'extensible.agendabodyview', id: this.id+'-bd', ownerCalendarPanel: this.ownerCalendarPanel, ownerCalendarView: this }, cfg); this.items = [header, body]; - this.addCls('ext-cal-list ext-cal-ct'); + this.addCls('ext-cal-agenda ext-cal-ct'); this.callParent(arguments); }, @@ -104,7 +104,7 @@ Ext.define('Extensible.calendar.view.List', { // private refresh : function(){ - Extensible.log('refresh (ListView)'); + Extensible.log('refresh (AgendaView)'); // this.header.refresh(); this.body.refresh(); }, @@ -148,7 +148,7 @@ Ext.define('Extensible.calendar.view.List', { /* Ext.defer(function(){ var ct = this.el.up('.x-panel-body'), - hd = this.el.down('.ext-cal-list-header'), + hd = this.el.down('.ext-cal-agenda-header'), h = ct.getHeight() - hd.getHeight(); this.el.down('.ext-cal-body-ct').setHeight(h-1); diff --git a/src/calendar/view/ListBody.js b/src/calendar/view/AgendaBody.js similarity index 93% rename from src/calendar/view/ListBody.js rename to src/calendar/view/AgendaBody.js index 30f833e4..8d201c60 100644 --- a/src/calendar/view/ListBody.js +++ b/src/calendar/view/AgendaBody.js @@ -1,28 +1,28 @@ /** - * @class Extensible.calendar.view.ListBody + * @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 list view. Normally you should not need to use this class directly - * -- instead you should use {@link Extensible.calendar.view.List List} view which aggregates this class and the - * {@link Extensible.calendar.view.ListHeader ListHeader} view into a single unified view + *

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.ListBody}.

+ * 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.ListBody', { +Ext.define('Extensible.calendar.view.AgendaBody', { extend: 'Extensible.calendar.view.AbstractCalendar', - alias: 'widget.extensible.listbodyview', + alias: 'widget.extensible.agendabodyview', requires: [ 'Ext.XTemplate', - 'Extensible.calendar.template.ListBody' + 'Extensible.calendar.template.AgendaBody' ], /** @@ -55,8 +55,8 @@ Ext.define('Extensible.calendar.view.ListBody', { */ dayLinkSelector: '.ext-cal-day-link', dayLinkIdDelimiter: 'ext-cal-day-', - prevLinkSelector: 'ext-cal-list-bd-prev-link', - nextLinkSelector: 'ext-cal-list-bd-next-link', + prevLinkSelector: 'ext-cal-agenda-bd-prev-link', + nextLinkSelector: 'ext-cal-agenda-bd-next-link', flex: 1, autoScroll: true, padding: '10 0 10 0', @@ -73,7 +73,7 @@ Ext.define('Extensible.calendar.view.ListBody', { /** * @event dayclick * Fires after the user clicks on a day date - * @param {Extensible.calendar.view.ListBody} this + * @param {Extensible.calendar.view.AgendaBody} this * @param {Date} dt The date that was clicked on. */ dayclick: true @@ -182,7 +182,7 @@ Ext.define('Extensible.calendar.view.ListBody', { // private afterRender : function(){ if(!this.tpl){ - this.tpl = Ext.create('Extensible.calendar.template.ListBody', { + this.tpl = Ext.create('Extensible.calendar.template.AgendaBody', { id: this.id, linkDatesToDayView: this.linkDatesToDayView, defaultEventTitleText: this.defaultEventTitleText, @@ -191,7 +191,7 @@ Ext.define('Extensible.calendar.view.ListBody', { }); this.tpl.compile(); } - this.addCls('ext-cal-list-bd ext-cal-ct'); + this.addCls('ext-cal-agenda-bd ext-cal-ct'); this.callParent(arguments); }, @@ -218,7 +218,7 @@ Ext.define('Extensible.calendar.view.ListBody', { // private refresh : function(reloadData){ - Extensible.log('refresh (ListView)'); + Extensible.log('refresh (AgendaView)'); this.callParent(arguments); }, @@ -330,7 +330,7 @@ Ext.define('Extensible.calendar.view.ListBody', { var el; // Handle click on an existing event - if(Extensible.calendar.view.ListBody.superclass.onClick.apply(this, arguments)){ + if(Extensible.calendar.view.AgendaBody.superclass.onClick.apply(this, arguments)){ // The superclass handled the click already so exit return; } diff --git a/src/calendar/view/ListHeader.js b/src/calendar/view/AgendaHeader.js similarity index 93% rename from src/calendar/view/ListHeader.js rename to src/calendar/view/AgendaHeader.js index 196fb32c..1b6794ef 100644 --- a/src/calendar/view/ListHeader.js +++ b/src/calendar/view/AgendaHeader.js @@ -1,12 +1,12 @@ /** - * @class Extensible.calendar.view.ListHeader + * @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.List List} view. Normally you should - * not need to use this class directly -- instead you should use {@link Extensible.calendar.view.List List} view which - * aggregates this class and the {@link Extensible.calendar.view.ListBody ListBody} view into a single unified view + *

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 @@ -21,9 +21,9 @@ * @constructor * @param {Object} config The config object */ -Ext.define('Extensible.calendar.view.ListHeader', { +Ext.define('Extensible.calendar.view.AgendaHeader', { extend: 'Ext.form.Panel', - alias: 'widget.extensible.listheaderview', + alias: 'widget.extensible.agendaheaderview', requires: [ 'Ext.form.ComboBox', @@ -137,7 +137,7 @@ Ext.define('Extensible.calendar.view.ListHeader', { xtype: 'toolbar', dock: 'bottom', ui: 'default', - cls: 'ext-cal-list-hd-tb', + cls: 'ext-cal-agenda-hd-tb', items: tbItems }]; } @@ -152,7 +152,7 @@ Ext.define('Extensible.calendar.view.ListHeader', { /** * @event formchange * Fires after the filter form changes. - * @param {Extensible.calendar.view.ListHeader} this + * @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. @@ -164,7 +164,7 @@ Ext.define('Extensible.calendar.view.ListHeader', { /** * @event addevent * Fires after the user clicks the add event button. - * @param {Extensible.calendar.view.ListHeader} this + * @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}. @@ -286,7 +286,7 @@ Ext.define('Extensible.calendar.view.ListHeader', { /* Private * Event handler that is called when the user clicks on the add event button. - * @param {Extensible.calendar.view.ListHeader} this + * @param {Extensible.calendar.view.AgendaHeader} this * @param {Ext.button.Button} bt * @param {Event} e * @param {Object} eOpts @@ -297,18 +297,18 @@ Ext.define('Extensible.calendar.view.ListHeader', { // private afterRender : function(){ - this.addCls('ext-cal-list-hd'); + this.addCls('ext-cal-agenda-hd'); this.callParent(arguments); }, // private refresh : function(reloadData){ - Extensible.log('refresh (ListHeader)'); + Extensible.log('refresh (AgendaHeader)'); this.callParent(arguments); }, /** - * This method is called by the {@link Extensible.calendar.view.List List} view that hosts this header when the user chooses to + * 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. diff --git a/src/locale/extensible-lang-de.js b/src/locale/extensible-lang-de.js index 42fb0bb6..15472a6b 100644 --- a/src/locale/extensible-lang-de.js +++ b/src/locale/extensible-lang-de.js @@ -40,7 +40,7 @@ Ext.onReady(function() { dayText: 'Tag', weekText: 'Woche', monthText: 'Monat', - listText: 'Liste', + agendaText: 'Agenda', jumpToText: 'Springe zu:', goText: 'Los', multiDayText: '{0} Tage', @@ -363,8 +363,8 @@ Ext.onReady(function() { /* * Strings for agenda view, added in x.x.x */ - if (exists('Extensible.calendar.template.ListBody')) { - Ext.apply(Extensible.calendar.template.ListBody.prototype, { + if (exists('Extensible.calendar.template.AgendaBody')) { + Ext.apply(Extensible.calendar.template.AgendaBody.prototype, { dayDateFormat: 'D. j. M.', hourFormat: 'G:i', allDayText: 'Ganzer Tag', @@ -379,8 +379,8 @@ Ext.onReady(function() { }); } - if (exists('Extensible.calendar.view.ListHeader')) { - Ext.apply(Extensible.calendar.view.ListHeader.prototype, { + if (exists('Extensible.calendar.view.AgendaHeader')) { + Ext.apply(Extensible.calendar.view.AgendaHeader.prototype, { dateRangeOneDay: 'Ein Tag', dateRangeOneWeek: 'Eine Woche', dateRangeOneMonth: 'Ein Monat', diff --git a/src/locale/extensible-lang-en.js b/src/locale/extensible-lang-en.js index 17ecc2a3..93e35c2f 100644 --- a/src/locale/extensible-lang-en.js +++ b/src/locale/extensible-lang-en.js @@ -46,7 +46,7 @@ Ext.onReady(function() { dayText: 'Day', weekText: 'Week', monthText: 'Month', - listText: 'List', + agendaText: 'Agenda', jumpToText: 'Jump to:', goText: 'Go', multiDayText: '{0} Days', // deprecated @@ -367,9 +367,8 @@ Ext.onReady(function() { /* * Strings for agenda view. Added in x.x.x */ - - if (exists('Extensible.calendar.template.ListBody')) { - Ext.apply(Extensible.calendar.template.ListBody.prototype, { + if (exists('Extensible.calendar.template.AgendaBody')) { + Ext.apply(Extensible.calendar.template.AgendaBody.prototype, { dayDateFormat: 'D M j', hourFormat: 'g:ia', allDayText: 'All day', @@ -384,8 +383,8 @@ Ext.onReady(function() { }); } - if (exists('Extensible.calendar.view.ListHeader')) { - Ext.apply(Extensible.calendar.view.ListHeader.prototype, { + if (exists('Extensible.calendar.view.AgendaHeader')) { + Ext.apply(Extensible.calendar.view.AgendaHeader.prototype, { dateRangeOneDay: 'One day', dateRangeOneWeek: 'One week', dateRangeOneMonth: 'One month', From a0fb75bd049f86388c82caab1eaf36b6fb044777 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sat, 15 Jun 2013 19:41:50 +0200 Subject: [PATCH 09/29] Adjustments needed after rebasing to latest version of recurrence --- examples/calendar/remote/remote.js | 3 ++- src/calendar/view/Agenda.js | 3 +++ src/calendar/view/AgendaBody.js | 7 +++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/calendar/remote/remote.js b/examples/calendar/remote/remote.js index 3a0505c1..3a18d32b 100644 --- a/examples/calendar/remote/remote.js +++ b/examples/calendar/remote/remote.js @@ -165,7 +165,8 @@ Ext.onReady(function() { eventStore: eventStore, calendarStore: calendarStore, title: 'Remote Calendar', - showAgendaView: true + showAgendaView: true, + activeItem: 3 // month view }); Ext.create('Ext.container.Viewport', { diff --git a/src/calendar/view/Agenda.js b/src/calendar/view/Agenda.js index 893332ea..b7b17e5f 100644 --- a/src/calendar/view/Agenda.js +++ b/src/calendar/view/Agenda.js @@ -57,6 +57,9 @@ Ext.define('Extensible.calendar.view.Agenda', { align: 'stretch' }, + // private + isAgendaView: true, + // private initComponent : function(){ diff --git a/src/calendar/view/AgendaBody.js b/src/calendar/view/AgendaBody.js index 8d201c60..257a2e02 100644 --- a/src/calendar/view/AgendaBody.js +++ b/src/calendar/view/AgendaBody.js @@ -350,5 +350,12 @@ Ext.define('Extensible.calendar.view.AgendaBody', { this.ownerCalendarView.moveNext(true); } + }, + + // inherited docs + isActiveView: function() { + var calendarPanel = this.ownerCalendarPanel; + return (calendarPanel && calendarPanel.getActiveView().isAgendaView); } + }); \ No newline at end of file From a76153d111b1c83e67b8c12d8b18981f11be1c13 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sat, 15 Jun 2013 19:42:03 +0200 Subject: [PATCH 10/29] First version of simple event list with support for grouping by month and week. --- Extensible-config-orig.js | 208 ------------- examples/calendar/TestApp/App.js | 9 + examples/calendar/basic.js | 4 +- examples/calendar/custom-mappings.js | 1 + examples/calendar/doc-types.js | 1 + examples/calendar/localization.js | 5 + examples/calendar/remote/recurrence.js | 14 + examples/calendar/remote/remote.js | 19 +- examples/calendar/tabpanel.js | 9 +- examples/calendar/window.js | 1 + resources/css/calendar.css | 14 + src/calendar/CalendarPanel.js | 62 +++- src/calendar/template/AgendaBody.js | 301 ++++++++++++++++--- src/calendar/view/Agenda.js | 35 ++- src/calendar/view/AgendaBody.js | 397 ++++++++++++++++++++++--- src/calendar/view/AgendaHeader.js | 155 ++++++++-- src/locale/extensible-lang-de.js | 5 + src/locale/extensible-lang-en.js | 5 + 18 files changed, 919 insertions(+), 326 deletions(-) delete mode 100644 Extensible-config-orig.js diff --git a/Extensible-config-orig.js b/Extensible-config-orig.js deleted file mode 100644 index 2135b54d..00000000 --- a/Extensible-config-orig.js +++ /dev/null @@ -1,208 +0,0 @@ -Extensible = { - version: '1.5.1' -}; -/** - * 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 the proper paths directly. - */ -Extensible.Config = { - /** - * Edit the values of these default configs to customize how Ext JS and Extensible are loaded. - */ - 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. - * - * 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.0.7 from the Sencha CDN via - * 'http://cdn.sencha.io/ext-4.0.7-gpl/'). Path should be absolute and should end with a '/'. - * - * Note that the Sencha CDN does not always provide the most current version of Ext JS - * available (for example, support subscribers often have access to more up-to-date builds). - * If the version you need is not hosted you'll have to download it locally and update this - * path accordingly. - * - * Alternate example values: - * - * // Older Ext JS versions: - * http://cdn.sencha.io/ext-4.0.2/ - * - * // Direct to cachefly.net, e.g. if sencha.io is down: - * http://extjs.cachefly.net/ext-4.0.7-gpl/ - * - * // A custom absolute path: - * http://localhost/extjs/ - * http://mydomain/extjs/4.0.2/ - * - * @config {String} extJsRoot - */ - extJsRoot: 'http://cdn.sencha.io/ext-4.0.7-gpl/', - - /** - * 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 - * to the url to enforce reloading Extensible files with each page refresh (useful if you are - * 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. - * - * 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 - }, - - /** - * 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.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) { - // If hosted at ext.ensible.com force non-debug release build includes - 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)), - suffix = '', - bootstrap = ''; - - switch (me.mode) { - case 'debug': - suffix = '-all-debug'; - break; - - case 'release': - suffix = '-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) { - suffix = '-all-debug'; - } - else { - bootstrap = '-bootstrap'; - } - } - - 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' + suffix + '.js'); - me.includeScript(me.extensibleRoot + 'lib/extensible' + suffix + bootstrap + '.js' + cacheBuster); - me.includeScript(me.extensibleRoot + 'examples/examples.js?_dc=' + Extensible.version); - } -}; - -/* - * Kick it off. To customize the configuration settings from external code, you can create a global - * object -- before including this Extensible-config.js script -- called "ExtensibleDefaults" and give - * it properties corresponding to the Extensible.Config configs you want to set. If it exists the - * ExtensibleDefaults object will be used and then destroyed automatically, otherwise the Extensible.Config - * defaults will be used. Any options not specified in the ExtensibleDefaults object will simply use the - * default value. For example: - * - * ExtensibleDefaults = { - * mode: 'dynamic' - * } - * - * Note that this global config object is primarily supported for testability and for one-off - * overriding of Extensible.Config. To change the configuration that you plan to use for your normal - * day-to-day use you should simply edit the Extensible.Config.defaults as needed to change the - * settings globally without having to create this object on each page. - */ -Extensible.Config.init(); - -// Clean up the global config override if it exists -try { - delete window.ExtensibleDefaults; -} -catch(ex) { - window.ExtensibleDefaults = null; -} diff --git a/examples/calendar/TestApp/App.js b/examples/calendar/TestApp/App.js index 38cbc089..a83939df 100644 --- a/examples/calendar/TestApp/App.js +++ b/examples/calendar/TestApp/App.js @@ -105,6 +105,7 @@ Ext.define('Extensible.example.calendar.TestApp.App', { //viewStartHour: 6, //viewEndHour: 18, //minEventDisplayMinutes: 15 + startDay: 0, showTime: false }, @@ -125,6 +126,13 @@ Ext.define('Extensible.example.calendar.TestApp.App', { dateRangeDefault: '3months' }, + listViewCfg: { + linkDatesToDayView: true, + dateRangeDefault: '3months', + simpleList: true, + groupBy: 'month' + }, + // Some optional CalendarPanel configs to experiment with: //readOnly: true, //showDayView: false, @@ -133,6 +141,7 @@ Ext.define('Extensible.example.calendar.TestApp.App', { //showMultiWeekView: false, //showMonthView: false, showAgendaView: true, + showListView: true, //showNavBar: false, //showTodayText: false, //showTime: false, diff --git a/examples/calendar/basic.js b/examples/calendar/basic.js index c36e4cfa..f4a8c7f2 100644 --- a/examples/calendar/basic.js +++ b/examples/calendar/basic.js @@ -28,7 +28,8 @@ Ext.onReady(function(){ width: 700, height: 500, activeItem: 3, // default to month view - showAgendaView: true + showAgendaView: true, + showListView: true }); // @@ -41,6 +42,7 @@ Ext.onReady(function(){ 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 dcd016a4..9d2e5abd 100644 --- a/examples/calendar/custom-mappings.js +++ b/examples/calendar/custom-mappings.js @@ -82,6 +82,7 @@ Ext.onReady(function(){ 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/doc-types.js b/examples/calendar/doc-types.js index 879968a2..8eb2c152 100644 --- a/examples/calendar/doc-types.js +++ b/examples/calendar/doc-types.js @@ -75,6 +75,7 @@ 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 1394e15b..2d2f9ae7 100644 --- a/examples/calendar/localization.js +++ b/examples/calendar/localization.js @@ -108,6 +108,11 @@ Ext.onReady(function() { 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 diff --git a/examples/calendar/remote/recurrence.js b/examples/calendar/remote/recurrence.js index f7f0e19b..18f56d88 100644 --- a/examples/calendar/remote/recurrence.js +++ b/examples/calendar/remote/recurrence.js @@ -202,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 3a18d32b..1a8986ba 100644 --- a/examples/calendar/remote/remote.js +++ b/examples/calendar/remote/remote.js @@ -166,7 +166,24 @@ Ext.onReady(function() { calendarStore: calendarStore, title: 'Remote Calendar', showAgendaView: true, - activeItem: 3 // month view + 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: 'week' + } }); Ext.create('Ext.container.Viewport', { diff --git a/examples/calendar/tabpanel.js b/examples/calendar/tabpanel.js index 9794667e..77924c1d 100644 --- a/examples/calendar/tabpanel.js +++ b/examples/calendar/tabpanel.js @@ -34,9 +34,16 @@ Ext.onReady(function(){ 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 a6156ef4..c50b6006 100644 --- a/examples/calendar/window.js +++ b/examples/calendar/window.js @@ -28,6 +28,7 @@ Ext.onReady(function(){ 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') diff --git a/resources/css/calendar.css b/resources/css/calendar.css index e14474a5..335f840f 100644 --- a/resources/css/calendar.css +++ b/resources/css/calendar.css @@ -795,6 +795,20 @@ td.ext-cal-dtitle-today div { 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; +} + /******************************************* * diff --git a/src/calendar/CalendarPanel.js b/src/calendar/CalendarPanel.js index 9c7741a3..630bb5df 100644 --- a/src/calendar/CalendarPanel.js +++ b/src/calendar/CalendarPanel.js @@ -64,6 +64,13 @@ Ext.define('Extensible.calendar.CalendarPanel', { * 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 @@ -162,6 +169,11 @@ Ext.define('Extensible.calendar.CalendarPanel', { * 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 @@ -228,7 +240,14 @@ Ext.define('Extensible.calendar.CalendarPanel', { */ /** * @cfg {Object} agendaViewCfg - * A config object that will be applied only to the {@link Extensible.calendar.view.Agenda AgendaView} managed by this CalendarPanel. + * 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 @@ -322,6 +341,12 @@ Ext.define('Extensible.calendar.CalendarPanel', { }); this.viewCount++; } + if(this.showListView){ + 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)); @@ -678,7 +703,35 @@ Ext.define('Extensible.calendar.CalendarPanel', { 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', @@ -1009,7 +1062,12 @@ Ext.define('Extensible.calendar.CalendarPanel', { 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/template/AgendaBody.js b/src/calendar/template/AgendaBody.js index 31fbc98b..55f205ff 100644 --- a/src/calendar/template/AgendaBody.js +++ b/src/calendar/template/AgendaBody.js @@ -18,6 +18,27 @@ Ext.define('Extensible.calendar.template.AgendaBody', { 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.

+ */ + simpleList: false, + + /** + * @cfg {String} groupBy + *

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'). @@ -105,7 +126,30 @@ Ext.define('Extensible.calendar.template.AgendaBody', { Ext.apply(this, config); - Extensible.calendar.template.AgendaBody.superclass.constructor.call(this, + // 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 [ '
', this.locationText, ':
', '', '', @@ -113,26 +157,18 @@ Ext.define('Extensible.calendar.template.AgendaBody', { // '', '', '', // events is a MixedCollection '', '', '', - '', + '', '
{[Ext.Date.format(values.date, \"D M j\")]}', '{[Ext.Date.format(values.date, this.dayDateFormat)]}', '
{[this.getEventTimesMarkup(values, parent.date)]}{[this.getEventTimesMarkupForAgendaList(values, parent.date)]}', - '', - // Display a one-line summary of the event - '{[this.getTitleMarkup(values)]}', - '{[this.getReminderFlagMarkup(values)]}', - '{[this.getRecurrenceFlagMarkup(values)]}', - '', + '{[this.getTitleMarkup(values)]}', '', // Display event with all details - '{[this.getTitleMarkup(values)]}', - '{[this.getReminderFlagMarkup(values)]}', - '{[this.getRecurrenceFlagMarkup(values)]}', '', '', '', @@ -170,41 +206,92 @@ Ext.define('Extensible.calendar.template.AgendaBody', { '
', this.prevLinkText, ' | ', this.nextLinkText, '


', '', '', - '
', - { - eventHasLocation: function(evt) { - var l = evt.data[Extensible.calendar.data.EventMappings.Location.name]; - return l && l != ""; - }, - eventHasLink: function(evt) { - var url = evt.data[Extensible.calendar.data.EventMappings.Url.name]; - return url && url != ""; - }, - eventHasNotes: function(evt) { - var n = evt.data[Extensible.calendar.data.EventMappings.Notes.name]; - return n && n != ""; - } - } - ); + '
' + ]; }, - // 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 simple list. + * @return {Array} A array of strings making up the template. + */ + getTemplateForSimpleList: function() { + return [ + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', // events is a MixedCollection + '', + '', + '', + '{[this.getEventTimesMarkupForSimpleList(values, parent.date)]}', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '
{[this.getGroupHeaderMarkup(values)]}

', + '{[Ext.Date.format(values.date, this.dayDateFormat)]}', + '
', + '{[this.getTitleMarkup(values)]}', + '', + // Display event with all details + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '
', this.locationText, ':{[values.data[Extensible.calendar.data.EventMappings.Location.name]]}
', this.webLinkText, ':{[this.getWebLinkMarkup(values, true)]}
', this.notesText, ':{[this.getNotesMarkup(values)]}
', + '
', + '

', + this.noEventsText, + '
', + '
', this.prevLinkText, ' | ', this.nextLinkText, '


', + '
' + ]; }, /** - * Returns event start and end times formatted for output. + * 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} */ - getEventTimesMarkup: function(evt, dt) { + getEventTimesMarkupForAgendaList: function(evt, dt) { var result, M = Extensible.calendar.data.EventMappings, currentDt = Ext.Date.clearTime(dt), @@ -243,11 +330,77 @@ Ext.define('Extensible.calendar.template.AgendaBody', { }, /** + * 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} - '{[this.getTitleMarkup(values)]}', - '{[values.data[Extensible.calendar.data.EventMappings.Location.name] != "" ? " - " + values.data[Extensible.calendar.data.EventMappings.Location.name] : ""]}', */ getTitleMarkup: function(evt) { var result, @@ -256,6 +409,8 @@ Ext.define('Extensible.calendar.template.AgendaBody', { 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) { @@ -322,6 +477,72 @@ Ext.define('Extensible.calendar.template.AgendaBody', { 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; } }, diff --git a/src/calendar/view/Agenda.js b/src/calendar/view/Agenda.js index b7b17e5f..7c98543b 100644 --- a/src/calendar/view/Agenda.js +++ b/src/calendar/view/Agenda.js @@ -4,9 +4,21 @@ * *

This class is currently beta code and the API is still subject to change before the next release.

* - *

Agenda view display events as a sorted list, similar to the agenda view in Google calendar. It 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.

+ *

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 @@ -44,6 +56,21 @@ Ext.define('Extensible.calendar.view.Agenda', { */ hideMode: 'offsets', + /** + * @cfg {Boolean} simpleList + *

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.

+ */ + simpleList: false, + + /** + * @cfg {String} groupBy + *

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 @@ -79,6 +106,8 @@ Ext.define('Extensible.calendar.view.Agenda', { var body = Ext.applyIf({ xtype: 'extensible.agendabodyview', id: this.id+'-bd', + simpleList: this.simpleList, + groupBy: this.groupBy, ownerCalendarPanel: this.ownerCalendarPanel, ownerCalendarView: this }, cfg); diff --git a/src/calendar/view/AgendaBody.js b/src/calendar/view/AgendaBody.js index 257a2e02..973b0140 100644 --- a/src/calendar/view/AgendaBody.js +++ b/src/calendar/view/AgendaBody.js @@ -34,10 +34,25 @@ Ext.define('Extensible.calendar.view.AgendaBody', { /** * @cfg {String} dateRangeDefault * Defines the default value for the date range. Supported values are: day, week, month, - * 3months and year.Defaults to 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.

+ */ + simpleList: false, + + /** + * @cfg {String} groupBy + *

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} @@ -66,6 +81,7 @@ Ext.define('Extensible.calendar.view.AgendaBody', { this.filterConfig = { period: this.dateRangeDefault, + groupby: this.groupBy, details: false }; @@ -86,7 +102,11 @@ Ext.define('Extensible.calendar.view.AgendaBody', { renderTemplate : function(){ var templateParams = this.getTemplateParams(); - templateParams.days = this.getTemplateEventData(); + 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); @@ -94,55 +114,234 @@ Ext.define('Extensible.calendar.view.AgendaBody', { } }, - /* - * Returns the template event data for rendering. The returned value is an array of days containing an array of events. - */ - getTemplateEventData : function(){ + /** + *

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.

+ *

+[
+    group1,
+    group2,
+    {
+        startDt: ,
+        endDt: ,
+        weekNo: ,   // Exists only if grouped by week
+        weekYear: , // Exists only if grouped by week
+        days: [
+            day1,
+            day2,
+            {
+                date: date,
+                events: [
+                    event1,
+                    event2,
+                    ....
+                ]
+            },
+            day3,
+            ....
+        ]
+    },
+    group3,
+    ....
+]
+     * 
+ * + * @return {Array} + */ + getTemplateEventDataForSimpleList : function(){ var M = Extensible.calendar.data.EventMappings, - calM = Extensible.calendar.data.CalendarMappings, events, - days = {}, - daysArray = [], - startDtView, endDtView, - extraClasses, - colorCls = 'x-cal-default', + event, + groups = {}, + groupsArray = [], + daysArray, + viewBounds = this.getViewBounds(), + startDtView = viewBounds.start, + endDtView = viewBounds.end, + startDtGroup, endDtGroup, + startDtEvent, endDtEvent, Dt = Extensible.Date, - viewBounds; + group, + groupKey, + dayKey, + weekInfo; - events = this.store.queryBy(function(rec){ - return this.isEventVisible(rec.data); - }, this); + // 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; + } - // Get start and end date of view period - viewBounds = this.getViewBounds(); - startDtView = viewBounds.start; - endDtView = viewBounds.end; + // Check if we have reached the end of the viewing period + if (startDtGroup.getTime() > endDtEvent.getTime() || startDtGroup.getTime() > endDtView.getTime()) { + break; + } - // Loop over all events of period and generate list items. Single-day events produce one item per event. - // Multi-day events produce multiple items, one for each day. - for (var i=0; iReturns 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.

+ *

+[
+    day1,
+    day2,
+    {
+        date: date,
+        events: [
+            event1,
+            event2,
+            ....
+        ]
+    },
+    day3,
+    ....
+]
+    * 
+ * + * @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; @@ -179,11 +378,118 @@ Ext.define('Extensible.calendar.view.AgendaBody', { 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
+     * 
+ * + *

For more information see + * http://en.wikipedia.org/wiki/Week_number#Week_numbering and + * + * http://www.pjh2.de/datetime/weeknumber/wnd.php?l=en#Legend.

+ * + * @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, @@ -222,7 +528,7 @@ Ext.define('Extensible.calendar.view.AgendaBody', { 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}. @@ -238,6 +544,8 @@ Ext.define('Extensible.calendar.view.AgendaBody', { 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; }, /** @@ -354,8 +662,9 @@ Ext.define('Extensible.calendar.view.AgendaBody', { // inherited docs isActiveView: function() { - var calendarPanel = this.ownerCalendarPanel; - return (calendarPanel && calendarPanel.getActiveView().isAgendaView); + 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 index 1b6794ef..ae4bbe2d 100644 --- a/src/calendar/view/AgendaHeader.js +++ b/src/calendar/view/AgendaHeader.js @@ -38,6 +38,13 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { * A reference to the calendar view that hosts this view. Read-only. */ + /** + * @cfg {Boolean} simpleList + *

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). @@ -79,6 +86,30 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { */ 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. @@ -104,6 +135,13 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { */ 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. @@ -124,6 +162,7 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { var tbItems = this.getToolbarConfig(); this.dateRangeOptions = this.getDateRangeOptions(); + this.groupByOptions = this.getGroupByOptions(); this.items = this.getFormConfig(); if (this.items.length == 0) { @@ -180,31 +219,71 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { * @return {Array} An array of Object */ getFormConfig: function() { - return [{ - xtype: 'combo', - id: this.id+'-daterange', - mode: 'local', - value: this.dateRangeDefault, - triggerAction: 'all', - forceSelection: true, - editable: false, - width: 220, - 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} 
', - listeners: { - change: {fn: this.onFormChange, scope: this} - } - },{ + 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} 
', + listeners: { + change: {fn: this.onFormChange, scope: this} + } + }] + }; + + 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: '
  • {name} 
', + listeners: { + change: {fn: this.onFormChange, scope: this} + } + }); + } + + formItems.items.push({ xtype: 'checkboxfield', id: this.id+'-showdetails', value: this.showDetailsDefault, @@ -214,7 +293,9 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { listeners: { change: {fn: this.onFormChange, scope: this} } - }]; + }); + + return [formItems]; }, /** @@ -273,6 +354,28 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { ]; }, + /** + *

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:


+     [
+     {name : 'None',   value: 'none'},
+     {name : 'Month',  value: 'month'},
+     {name : 'Week',  value: 'week'}
+     ]
+     

+ * @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 diff --git a/src/locale/extensible-lang-de.js b/src/locale/extensible-lang-de.js index 15472a6b..5b9fcf57 100644 --- a/src/locale/extensible-lang-de.js +++ b/src/locale/extensible-lang-de.js @@ -41,6 +41,7 @@ Ext.onReady(function() { weekText: 'Woche', monthText: 'Monat', agendaText: 'Agenda', + listText: 'Liste', jumpToText: 'Springe zu:', goText: 'Los', multiDayText: '{0} Tage', @@ -387,6 +388,10 @@ Ext.onReady(function() { 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' diff --git a/src/locale/extensible-lang-en.js b/src/locale/extensible-lang-en.js index 93e35c2f..8e35a2c1 100644 --- a/src/locale/extensible-lang-en.js +++ b/src/locale/extensible-lang-en.js @@ -47,6 +47,7 @@ Ext.onReady(function() { weekText: 'Week', monthText: 'Month', agendaText: 'Agenda', + listText: 'List', jumpToText: 'Jump to:', goText: 'Go', multiDayText: '{0} Days', // deprecated @@ -391,6 +392,10 @@ Ext.onReady(function() { 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' From 032b3f1b7bcb3a9be421cc9e785bf8dc55b0a92f Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sat, 15 Jun 2013 19:42:22 +0200 Subject: [PATCH 11/29] Minor adjustments to examples. --- examples/calendar/remote/remote.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/calendar/remote/remote.js b/examples/calendar/remote/remote.js index 1a8986ba..e60098f1 100644 --- a/examples/calendar/remote/remote.js +++ b/examples/calendar/remote/remote.js @@ -182,7 +182,7 @@ Ext.onReady(function() { listViewCfg: { linkDatesToDayView: true, dateRangeDefault: '3months', - groupBy: 'week' + groupBy: 'month' } }); From ea10f0ff03b7d40366bbd9b4e1d46c38f1efb727 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sat, 15 Jun 2013 19:42:37 +0200 Subject: [PATCH 12/29] Added plus-icon to "Add Event" button on agenda view. --- resources/css/calendar.css | 4 ++++ resources/images/default/ext/add.gif | Bin 0 -> 994 bytes src/calendar/view/AgendaHeader.js | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 resources/images/default/ext/add.gif diff --git a/resources/css/calendar.css b/resources/css/calendar.css index 335f840f..9db93d6e 100644 --- a/resources/css/calendar.css +++ b/resources/css/calendar.css @@ -773,6 +773,10 @@ td.ext-cal-dtitle-today div { 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; diff --git a/resources/images/default/ext/add.gif b/resources/images/default/ext/add.gif new file mode 100644 index 0000000000000000000000000000000000000000..93195256dc0abb6a451c41be3cba50fabb28781f GIT binary patch literal 994 zcmZ?wbhEHb6krfwXlG!sZ8LT1HFNH_aOtsj?X~gjxA*9E^z3&Ep6U}i%{O4CWB5GR zxH(~o^CD6fgr+PAPg@j`zBoE{b!f)w;OtcqS!<$mRz>A)jmQU~$dc{RCEH^Pc0?BK zj4s|4Q@Ag_Y)yK_x{UHY2^CvX>NjQ8>`JNKlUBPgy>f3}?ar*)o!Rv}a~=LW6dX*h zJeX2-EW74#cHP0uy5spxTl1Q?=Cy4v?ATG>v%9QoXJzNUir#(IllRv*o+)iRS=Dy3 zruA%P*P+V(L$#eJ>buU@PTk+sbGotbOiSOT=1E5zrXFaTe!O-1nbyf?J7%12n{lRn z#+k0!2dB?IIAhA?u4$J$r(WrrcBOmz)$W-WyXW4RwB+2BW#^|YyD)9R&8Z7+PFs3= z_Nps$S6`X4?8JiQCl{{2zjXcmWt(p<-2z0nmTkJdV)NaVTW_z}erwg1yQ{X|U$gD* znjKfy@3^~u_nq~-u5H}@VEw*F+x9-$cI3{Eqjz^6eR1IQv%}|Jp1kt<{PlO2?tHxQ z;M1M||Nk?Lf&mQy#h)z93=BFz2n3)!!N9SfA(BJJW5a@j)}C2s4k#Xfa6mTl)0>OT z>8>2@=8t}?e7y9ugpGrO$eoSL_{G%RRs~oZxx2PBmQDHaP|5Yge0%$Z6B(NwdK{GU zPKbPLe3Zs2D7Q`_Q(*#|thQ~Q*Wbg6$EGtec`lJWl>EwVMicT~*I!ki1b6H6&Jl5GM Date: Sat, 15 Jun 2013 19:42:51 +0200 Subject: [PATCH 13/29] Fix small syntax error. --- src/locale/extensible-lang-en.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/locale/extensible-lang-en.js b/src/locale/extensible-lang-en.js index 8e35a2c1..1cb746a2 100644 --- a/src/locale/extensible-lang-en.js +++ b/src/locale/extensible-lang-en.js @@ -364,6 +364,8 @@ Ext.onReady(function() { nthWeekdayDateFormat: 'S' // displays the ordinal postfix, e.g. th for 5th. } + }); + } /* * Strings for agenda view. Added in x.x.x From a9bf55fa0a7546e0c80fe8a4c65828ffd65c482e Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sun, 30 Jun 2013 15:32:31 +0200 Subject: [PATCH 14/29] Ensure that at least one calendar view is shown. --- src/calendar/CalendarPanel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calendar/CalendarPanel.js b/src/calendar/CalendarPanel.js index 630bb5df..461e6abd 100644 --- a/src/calendar/CalendarPanel.js +++ b/src/calendar/CalendarPanel.js @@ -328,7 +328,7 @@ 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' }); @@ -341,7 +341,7 @@ Ext.define('Extensible.calendar.CalendarPanel', { }); this.viewCount++; } - if(this.showListView){ + 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' }); From 1978866dc1db921c7f9c2ec39d263b68ba813392 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Thu, 1 Aug 2013 19:17:24 +0200 Subject: [PATCH 15/29] Added a class to the next/prev links to be able to apply some printing style. --- src/calendar/template/AgendaBody.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calendar/template/AgendaBody.js b/src/calendar/template/AgendaBody.js index 55f205ff..bc934ea2 100644 --- a/src/calendar/template/AgendaBody.js +++ b/src/calendar/template/AgendaBody.js @@ -202,7 +202,7 @@ Ext.define('Extensible.calendar.template.AgendaBody', { this.noEventsText, '', '', - '', + '', '
', this.prevLinkText, ' | ', this.nextLinkText, '


', '', '', @@ -277,7 +277,7 @@ Ext.define('Extensible.calendar.template.AgendaBody', { this.noEventsText, '', '', - '', + '', '
', this.prevLinkText, ' | ', this.nextLinkText, '


', '', '', From 8d4c08561f2f2be113b06efb5be14caf3aae702b Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Sat, 14 Sep 2013 19:06:37 +0200 Subject: [PATCH 16/29] Added English GB file with strings for Agenda view. --- src/locale/extensible-lang-en_GB.js | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/locale/extensible-lang-en_GB.js b/src/locale/extensible-lang-en_GB.js index caecb686..6183c111 100644 --- a/src/locale/extensible-lang-en_GB.js +++ b/src/locale/extensible-lang-en_GB.js @@ -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 From d9fa57a3cbfe7fdceeb18d731dd09970dbfaae06 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Thu, 19 Sep 2013 22:22:51 +0200 Subject: [PATCH 17/29] Added support for client state persistency. --- src/calendar/view/Agenda.js | 2 + src/calendar/view/AgendaHeader.js | 83 +++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/calendar/view/Agenda.js b/src/calendar/view/Agenda.js index 7c98543b..fe9c794a 100644 --- a/src/calendar/view/Agenda.js +++ b/src/calendar/view/Agenda.js @@ -96,6 +96,8 @@ Ext.define('Extensible.calendar.view.Agenda', { 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}, diff --git a/src/calendar/view/AgendaHeader.js b/src/calendar/view/AgendaHeader.js index e8dc0a90..a441400f 100644 --- a/src/calendar/view/AgendaHeader.js +++ b/src/calendar/view/AgendaHeader.js @@ -149,6 +149,7 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { showDetailsDefault: false, // private configs + cls: 'ext-cal-agenda-hd', preventHeader: true, autoHeight: true, border: 0, @@ -249,10 +250,7 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { }), // 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} 
', - listeners: { - change: {fn: this.onFormChange, scope: this} - } + tpl: '
  • {name} 
' }] }; @@ -276,10 +274,7 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { }), // 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} 
', - listeners: { - change: {fn: this.onFormChange, scope: this} - } + tpl: '
  • {name} 
' }); } @@ -289,10 +284,7 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { value: this.showDetailsDefault, inputvalue: '1', fieldLabel: this.showDetailsText, - name: 'details', - listeners: { - change: {fn: this.onFormChange, scope: this} - } + name: 'details' }); return [formItems]; @@ -335,13 +327,13 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { *

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:


-[
+     [
      {name : 'One Day',   value: 'day'},
      {name : 'One Week',  value: 'week'},
      {name : 'One Month',  value: 'month'},
      {name : '3 Months',  value: '3months'},
      {name : 'One Year', value: 'year'}
-]
+     ]
      

* @return {Object} */ @@ -386,6 +378,7 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { */ onFormChange: function(field, newValue, oldValue, eOpts){ this.fireEvent('formchange', this, this.getForm(), field, newValue, oldValue, eOpts); + this.saveState(); }, /* Private @@ -401,8 +394,19 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { // private afterRender : function(){ - this.addCls('ext-cal-agenda-hd'); 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 @@ -418,6 +422,55 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { * @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 From 7fd6644a1b79b24c79fffee971e7a454fcf1f371 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Thu, 3 Apr 2014 18:58:11 +0200 Subject: [PATCH 18/29] New events are not all-day events by default. --- src/calendar/view/Agenda.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calendar/view/Agenda.js b/src/calendar/view/Agenda.js index fe9c794a..5a6b81be 100644 --- a/src/calendar/view/Agenda.js +++ b/src/calendar/view/Agenda.js @@ -169,7 +169,7 @@ Ext.define('Extensible.calendar.view.Agenda', { 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] = false; + data[M.IsAllDay.name] = true; this.body.showEventEditor(data, bt.getEl()); }, From 7a38d5effa39a69817d6449857b1704005cc32fe Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Thu, 3 Apr 2014 19:00:08 +0200 Subject: [PATCH 19/29] Updated build file to work with the build process refactored by Brian. --- build/resources/extensible.jsb2 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/resources/extensible.jsb2 b/build/resources/extensible.jsb2 index c9cd9eb8..a9da86d8 100644 --- a/build/resources/extensible.jsb2 +++ b/build/resources/extensible.jsb2 @@ -40,7 +40,7 @@ "path": "../../src/calendar/template/" },{ "text": "AgendaBody.js", - "path": "../src/calendar/template/" + "path": "../../src/calendar/template/" },{ "text": "CalendarScrollManager.js", "path": "../../src/calendar/dd/" @@ -133,13 +133,13 @@ "path": "../../src/calendar/view/" },{ "text": "AgendaHeader.js", - "path": "../src/calendar/view/" + "path": "../../src/calendar/view/" },{ "text": "AgendaBody.js", - "path": "../src/calendar/view/" + "path": "../../src/calendar/view/" },{ "text": "Agenda.js", - "path": "../src/calendar/view/" + "path": "../../src/calendar/view/" },{ "text": "CalendarPanel.js", "path": "../../src/calendar/" From 8356ea14efd965fa2eade1a5d79a3a57a15d27f3 Mon Sep 17 00:00:00 2001 From: gsidler Date: Thu, 16 Apr 2015 16:50:37 +0200 Subject: [PATCH 20/29] Migration to extjs5: Remove addEvent() calls. --- src/calendar/view/AgendaBody.js | 15 +++++------ src/calendar/view/AgendaHeader.js | 42 ++++++++++++++----------------- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/calendar/view/AgendaBody.js b/src/calendar/view/AgendaBody.js index 973b0140..6a2ce91b 100644 --- a/src/calendar/view/AgendaBody.js +++ b/src/calendar/view/AgendaBody.js @@ -85,15 +85,12 @@ Ext.define('Extensible.calendar.view.AgendaBody', { details: false }; - this.addEvents({ - /** - * @event dayclick - * Fires after the user clicks on a day date - * @param {Extensible.calendar.view.AgendaBody} this - * @param {Date} dt The date that was clicked on. - */ - dayclick: true - }); + /** + * @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); }, diff --git a/src/calendar/view/AgendaHeader.js b/src/calendar/view/AgendaHeader.js index a441400f..3d39f0b6 100644 --- a/src/calendar/view/AgendaHeader.js +++ b/src/calendar/view/AgendaHeader.js @@ -188,29 +188,25 @@ Ext.define('Extensible.calendar.view.AgendaHeader', { this.callParent(arguments); - this.addEvents({ - /** - * @event formchange - * Fires after the filter form changes. - * @param {Extensible.calendar.view.AgendaHeader} this - * @param {Ext.form.Basic} form The filter form. - * @param {Ext.form.field.Field} field Form field that changed. - * @param {Object} newValue New form field value. - * @param {Object} oldValue Old form field value. - * @param {Object} eOpts The options object passed to {@link Ext.util.Observable.addListener}. - */ - formchange: true, - - /** - * @event addevent - * Fires after the user clicks the add event button. - * @param {Extensible.calendar.view.AgendaHeader} this - * @param {Ext.button.Button} button The button clicked. - * @param {Event} event - * @param {Object} eOpts The options object passed to {@link Ext.util.Observable.addListener}. - */ - addevent: true - }); + /** + * @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}. + */ }, From c2e69d326d3e29f3344a021195e27da2df893c0b Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Thu, 3 Apr 2014 17:45:57 +0200 Subject: [PATCH 21/29] Cleanup dirty record when editor window is closed with close tool in panel header. --- src/calendar/form/EventWindow.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/calendar/form/EventWindow.js b/src/calendar/form/EventWindow.js index cb7a4cb1..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 From 11887f5ff796e08e93902b6ea22c5dcfe43cd434 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Wed, 9 Apr 2014 14:15:08 +0200 Subject: [PATCH 22/29] Fixed error handling after copying an event. If server rejects new event, the event needs to be removed from the store. --- src/calendar/view/AbstractCalendar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calendar/view/AbstractCalendar.js b/src/calendar/view/AbstractCalendar.js index 2a87f090..532ae982 100644 --- a/src/calendar/view/AbstractCalendar.js +++ b/src/calendar/view/AbstractCalendar.js @@ -1564,7 +1564,7 @@ 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(); From 2b2c9cbc5dc46d366171f3825ba2d893d83c3ca1 Mon Sep 17 00:00:00 2001 From: Gabriel Sidler Date: Mon, 19 May 2014 15:40:20 +0200 Subject: [PATCH 23/29] Re-insert events into event store if delete operation fails. --- src/calendar/view/AbstractCalendar.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/calendar/view/AbstractCalendar.js b/src/calendar/view/AbstractCalendar.js index 2a87f090..2266ed53 100644 --- a/src/calendar/view/AbstractCalendar.js +++ b/src/calendar/view/AbstractCalendar.js @@ -1571,7 +1571,22 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', { } } }, 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); } From 1c744fefc70a27aa041939dd29b904995b7e6882 Mon Sep 17 00:00:00 2001 From: gsidler Date: Fri, 30 Jan 2015 14:15:47 +0100 Subject: [PATCH 24/29] Improved sorting for hourly events. Events are sorted by three criteria: Start time, end time and calendar id. This garantees deterministic ordering of events and is a precondition for an improved event layout algorithm. --- src/calendar/view/AbstractCalendar.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/calendar/view/AbstractCalendar.js b/src/calendar/view/AbstractCalendar.js index 2a87f090..01a688da 100644 --- a/src/calendar/view/AbstractCalendar.js +++ b/src/calendar/view/AbstractCalendar.js @@ -1447,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)); }, From aaece75e1b302fb2fdcfdc8710b1b9bf1551877e Mon Sep 17 00:00:00 2001 From: gsidler Date: Fri, 30 Jan 2015 14:16:46 +0100 Subject: [PATCH 25/29] Implemented a improved event layout algorithm for day body view. See code comments for more details. --- src/calendar/view/DayBody.js | 144 +++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 40 deletions(-) diff --git a/src/calendar/view/DayBody.js b/src/calendar/view/DayBody.js index 0ee27a22..246a94fe 100644 --- a/src/calendar/view/DayBody.js +++ b/src/calendar/view/DayBody.js @@ -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) { From 6be34e87b527a7dbfabaf9ec48ebca4a73ab163a Mon Sep 17 00:00:00 2001 From: Cristian Bujoreanu Date: Tue, 10 Mar 2015 10:44:55 +0100 Subject: [PATCH 26/29] save state of calendars after refresh --- examples/calendar/TestApp/App.js | 6 ++- src/calendar/gadget/CalendarListPanel.js | 69 ++++++++++++++++++++---- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/examples/calendar/TestApp/App.js b/examples/calendar/TestApp/App.js index c2b18f5d..e86bbb94 100644 --- a/examples/calendar/TestApp/App.js +++ b/examples/calendar/TestApp/App.js @@ -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 diff --git a/src/calendar/gadget/CalendarListPanel.js b/src/calendar/gadget/CalendarListPanel.js index 904480a7..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,12 +136,12 @@ 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]; @@ -139,19 +151,24 @@ Ext.define('Extensible.calendar.gadget.CalendarListPanel', { 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); } }, @@ -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 From e26b9538c1260c2cd3afdcf576f67fa157e5f942 Mon Sep 17 00:00:00 2001 From: gsidler Date: Fri, 30 Jan 2015 15:32:50 +0100 Subject: [PATCH 27/29] Introduced new load mode 'dynamic-extensible'. With this mode, the extensible framework is loaded dynamically (class by class) while the ext/js is loaded from the archive file. Compared to the fully dynamic mode is is *much* faster. Furthermore, fixed loading of CSS files in debug and dynamic modes. In these modes, CSS files are now loaded individually, not the concatenated file. --- Extensible-config.js | 75 ++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/Extensible-config.js b/Extensible-config.js index a80601b3..2086a574 100644 --- a/Extensible-config.js +++ b/Extensible-config.js @@ -26,6 +26,9 @@ Extensible.Config = { * - '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 @@ -39,7 +42,7 @@ Extensible.Config = { * * @config {String} mode */ - mode: 'dynamic', + mode: 'debug', /** * The root path to the Ext JS framework (defaults to loading 4.2.0 from the Sencha CDN via @@ -122,8 +125,7 @@ Extensible.Config = { language: null, /** - * Name of the default theme - * Based by theme nema the path to resouces is built: ext-5.1.0\build\packages\ext-theme-$THEME + * Name of theme used. Supported values are: 'neptune', nepture-touch', 'crisp', 'crisp-touch'. */ theme: 'neptune' }, @@ -176,43 +178,40 @@ Extensible.Config = { // 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: - suffixExt = '-all-debug'; - // 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) { - suffixExtensible = '-all-debug'; - } - else { - 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'); - me.includeStylesheet(me.extensibleRoot + 'resources/css/extensible-all.css' + cacheBuster); - me.includeStylesheet(me.extensibleRoot + 'examples/examples.css?_dc=' + Extensible.version); - - me.includeScript(me.extJsRoot + 'build/' + 'ext' + suffixExt + '.js'); - me.includeScript(me.extensibleRoot + 'lib/extensible' + suffixExtensible + '.js' + cacheBuster); + 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.includeScript(me.extensibleRoot + 'examples/examples.js?_dc=' + Extensible.version); if (me.language) { From 6c7eadf76f70a7fe8a862a2c25adb24c43bda0ff Mon Sep 17 00:00:00 2001 From: gsidler Date: Fri, 30 Jan 2015 15:41:00 +0100 Subject: [PATCH 28/29] Set Ext.Loader property disableCaching to false for all the examples. For most examples it was false already, for a few not. It is confusing if caching is disabled for some examples and for some not. Furthermore, if caching is disabled, debugging with Firebug becomes really painful. --- examples/calendar/TestApp/App.js | 2 +- examples/calendar/remote/recurrence.js | 2 +- examples/calendar/remote/remote.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/calendar/TestApp/App.js b/examples/calendar/TestApp/App.js index c2b18f5d..ed64d9ee 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": "../.." diff --git a/examples/calendar/remote/recurrence.js b/examples/calendar/remote/recurrence.js index f7f0e19b..157c6f3d 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": "../../" diff --git a/examples/calendar/remote/remote.js b/examples/calendar/remote/remote.js index 6fd78163..deed30b8 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": "../../" From eb113c659f99f5e5c22ece785dfc9e229339462f Mon Sep 17 00:00:00 2001 From: gsidler Date: Fri, 30 Jan 2015 15:41:19 +0100 Subject: [PATCH 29/29] Small fix in the test data. --- examples/calendar/data/Events.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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" },{