diff --git a/build.xml b/build.xml index 505c017..367e265 100644 --- a/build.xml +++ b/build.xml @@ -1,4 +1,9 @@ + + + + + @@ -64,6 +69,12 @@ + + + + + + @@ -76,7 +87,7 @@ - + @@ -86,12 +97,14 @@ + + @@ -135,6 +148,7 @@ var Ext = {}; var Zarafa = {}; + var timezoneJS = {}; var container = {}; var _ = function(key, domain) {}; var dgettext = function(domain, msgid) {}; @@ -226,8 +240,10 @@ - + + + @@ -236,6 +252,9 @@ + + + diff --git a/changelog.txt b/changelog.txt index ad339d1..2b9214e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +calendarimporter 1.1, 26.02.2013: + - daylight saving time + - webapp 1.3 about text added + calendarimporter 1.2: - New timezone management - more imported fields (Busystatus, importance, label, class, organizer, reminder) diff --git a/index.html b/index.html new file mode 100644 index 0000000..93f96a6 --- /dev/null +++ b/index.html @@ -0,0 +1,124 @@ + + + + DST Calculator + + + + + + \ No newline at end of file diff --git a/js/ABOUT.js b/js/ABOUT.js new file mode 100644 index 0000000..15c8040 --- /dev/null +++ b/js/ABOUT.js @@ -0,0 +1,36 @@ +Ext.namespace('Zarafa.plugins.calendarimporter'); + +/** + * @class Zarafa.plugins.calendarimporter.ABOUT + * @extends String + * + * The copyright string holding the copyright notice for the Zarafa calendarimporter Plugin. + */ +Zarafa.plugins.calendarimporter.ABOUT = "" + + "

Copyright (C) 2012-2013 Christoph Haas <christoph.h@sprinternet.at>

" + + + "

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

" + + + "

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

" + + + "

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

" + + + "
" + + + "

The calendarimporter plugin contains the following third-party components:

" + + + "

TimezoneJS.Date

" + + + "

Copyright 2010 Matthew Eernisse <mde@fleegix.org> and Open Source Applications Foundation.

" + + + "

Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

" + + + "

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

" + + + "

Fleegix.js JavaScript Toolkit

" + + + "

Copyright 2002-2007 Matthew Eernisse <mde@fleegix.org> and Open Source Applications Foundation.

" + + + "

Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

" + + + "

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

" \ No newline at end of file diff --git a/js/data/timezones.js b/js/data/timezones.js index ea2bbb9..778f328 100644 --- a/js/data/timezones.js +++ b/js/data/timezones.js @@ -744,6 +744,10 @@ Zarafa.plugins.calendarimporter.data.Timezones = Ext.extend(Object, { } return 0; // no offset found... + }, + + getDstOffset: function(time, timezone) { + return 0; // no offset } }); diff --git a/js/dialogs/ImportPanel.js b/js/dialogs/ImportPanel.js index a7af20f..98f90e3 100644 --- a/js/dialogs/ImportPanel.js +++ b/js/dialogs/ImportPanel.js @@ -18,6 +18,9 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { /* store the imported timezone here... */ timezone: null, + /* store the imported timezone here... */ + dst: true, + /* keep the parsed result here, for timezone changes... */ parsedresult: null, @@ -52,6 +55,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { items : [ this.createSelectBox(), this.createTimezoneBox(), + this.createDaylightSavingCheckBox(), this.initForm() ], buttons: [ @@ -101,7 +105,9 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { this.remove("eventgrid"); var parsedData = []; - var local_tz_offset = new Date().getTimezoneOffset() * 60000; // getTimezoneOffset returns minutes... we need milliseconds + + /* this is used to get rid of the local timezone... */ + var local_tz_offset = new Date().getTimezoneOffset() * 60; // getTimezoneOffset returns minutes... we need milliseconds var tz_offset = local_tz_offset; if(this.timezone != null) { @@ -113,12 +119,26 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { var i = 0; for(i = 0; i < eventdata.events.length; i++) { var trigger = null; + var dtrigger = null; if(eventdata.events[i]["VALARM"]) { trigger = eventdata.events[i]["VALARM"]["TRIGGER"]; - trigger = new Date(parseInt(trigger) + local_tz_offset + tz_offset); + dtrigger = new timezoneJS.Date(parseInt(trigger) + local_tz_offset + tz_offset, "Etc/UTC"); + if(this.timezone !== null) { + dtrigger.setTimezone(this.timezone); + } } - parsedData[i] = new Array(eventdata.events[i]["SUMMARY"], new Date(parseInt(eventdata.events[i]["DTSTART"]) + local_tz_offset + tz_offset), new Date(parseInt(eventdata.events[i]["DTEND"]) + local_tz_offset + tz_offset), eventdata.events[i]["LOCATION"], eventdata.events[i]["DESCRIPTION"],eventdata.events[i]["PRIORITY"],eventdata.events[i]["X-ZARAFA-LABEL"],eventdata.events[i]["X-MICROSOFT-CDO-BUSYSTATUS"],eventdata.events[i]["CLASS"],eventdata.events[i]["ORGANIZER"],trigger); + + var dstart = new timezoneJS.Date(parseInt(eventdata.events[i]["DTSTART"]) + local_tz_offset + tz_offset, "Etc/UTC"); + var dend = new timezoneJS.Date(parseInt(eventdata.events[i]["DTEND"]) + local_tz_offset + tz_offset, "Etc/UTC"); + if(this.timezone !== null) { + dstart.setTimezone(this.timezone); + dend.setTimezone(this.timezone); + } + console.log(this.timezone); + console.log(dstart); + + parsedData[i] = new Array(eventdata.events[i]["SUMMARY"], dstart, dend, eventdata.events[i]["LOCATION"], eventdata.events[i]["DESCRIPTION"],eventdata.events[i]["PRIORITY"],eventdata.events[i]["X-ZARAFA-LABEL"],eventdata.events[i]["X-MICROSOFT-CDO-BUSYSTATUS"],eventdata.events[i]["CLASS"],eventdata.events[i]["ORGANIZER"], dtrigger); } } else { return null; @@ -159,8 +179,30 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { }, columns: [ {id: 'Summary', header: 'Title', width: 300, sortable: true, dataIndex: 'title'}, - {header: 'Start', width: 150, sortable: true, dataIndex: 'start', renderer : Zarafa.common.ui.grid.Renderers.datetime}, - {header: 'End', width: 150, sortable: true, dataIndex: 'end', renderer : Zarafa.common.ui.grid.Renderers.datetime}, + { + header: 'Start', + width: 150, + sortable: true, + dataIndex: 'start', + renderer : function(value, p, record) { + p.css = 'mail_date'; + + // # TRANSLATORS: See http://docs.sencha.com/ext-js/3-4/#!/api/Date for the meaning of these formatting instructions + return ((value !== null) && ((typeof value) == "object") && ((typeof value.getTime) == "function")) ? value.toString("yyyy-MM-dd HH:mm:ss Z") :_('None'); + } + }, + { + header: 'End', + width: 150, + sortable: true, + dataIndex: 'end', + renderer : function(value, p, record) { + p.css = 'mail_date'; + + // # TRANSLATORS: See http://docs.sencha.com/ext-js/3-4/#!/api/Date for the meaning of these formatting instructions + return ((value !== null) && ((typeof value) == "object") && ((typeof value.getTime) == "function")) ? value.toString("yyyy-MM-dd HH:mm:ss Z") :_('None'); + } + }, {header: 'Location', width: 150, sortable: true, dataIndex: 'location'}, {header: 'Description', width: 150, sortable: true, dataIndex: 'description'}, {header: "Priority", dataIndex: 'priority', hidden: true}, @@ -168,7 +210,17 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { {header: "Busystatus", dataIndex: 'busy', hidden: true}, {header: "Privacystatus", dataIndex: 'privatestate', hidden: true}, {header: "Organizer", dataIndex: 'organizer', hidden: true}, - {header: "Alarm", dataIndex: 'trigger', hidden: true, renderer : Zarafa.common.ui.grid.Renderers.datetime} + { + header: "Alarm", + dataIndex: 'trigger', + hidden: true, + renderer : function(value, p, record) { + p.css = 'mail_date'; + + // # TRANSLATORS: See http://docs.sencha.com/ext-js/3-4/#!/api/Date for the meaning of these formatting instructions + return ((value !== null) && ((typeof value) == "object") && ((typeof value.getTime) == "function")) ? value.toString("yyyy-MM-dd HH:mm:ss Z") :_('None'); + } + } ] }), sm: new Ext.grid.RowSelectionModel({multiSelect:true}) @@ -241,6 +293,26 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { } }, + createDaylightSavingCheckBox: function() { + return { + xtype: "checkbox", + ref: 'dstcheck', + id: 'dstcheck', + name: "dst_check", + width: 100, + fieldLabel: "Ignore Daylight Saving Time (optional)", + labelSeperator: ":", + border: false, + anchor: "100%", + scope: this, + allowBlank: true, + listeners: { + 'check': this.onDstChecked, + scope: this + } + } + }, + createUploadField: function() { return { xtype: "fileuploadfield", @@ -336,6 +408,21 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { } }, + /** + * This is called when the dst checkbox has been selected + * @param {Ext.form.ComboBox} combo + * @param {Ext.data.Record} record + * @param {Number} index + */ + onDstChecked : function(checkbox, checked) { + this.dst = !checked; + + if(this.parsedresult != null) { + this.add(this.createGrid(this.parsedresult)); + this.doLayout(); + } + }, + /** * This is called when a file has been seleceted in the file dialog * in the {@link Ext.ux.form.FileUploadField} and the dialog is closed @@ -667,3 +754,4 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { }); Ext.reg('calendarimporter.importpanel', Zarafa.plugins.calendarimporter.dialogs.ImportPanel); +; diff --git a/js/plugin.calendarimporter.js b/js/plugin.calendarimporter.js index 64ad96f..0f93c35 100644 --- a/js/plugin.calendarimporter.js +++ b/js/plugin.calendarimporter.js @@ -4,7 +4,7 @@ * Main entry point for the plugin * * @author Christoph Haas - * @modified 29.12.2012 + * @modified 26.02.2013 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ @@ -20,6 +20,12 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, { constructor: function (config) { config = config || {}; + Ext.applyIf(config, { + name : 'calendarimporter', + displayName : _('Calendarimporter Plugin'), + about : Zarafa.plugins.calendarimporter.ABOUT + }); + Zarafa.plugins.calendarimporter.ImportPlugin.superclass.constructor.call(this, config); }, @@ -28,6 +34,12 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, { * @protected */ initPlugin : function() { + + // First of all initialize timezone-js.... + timezoneJS.timezone.zoneFileBasePath = 'plugins/calendarimporter/resources/tz'; + timezoneJS.timezone.defaultZoneFile = @_@PLUGIN_TIMEZONES@_@; // replaced by buildscript -> https://github.com/mde/timezone-js + timezoneJS.timezone.init({async: false}); + Zarafa.plugins.calendarimporter.ImportPlugin.superclass.initPlugin.apply(this, arguments); Zarafa.core.data.SharedComponentType.addProperty('plugins.calendarimporter.dialogs.importevents'); diff --git a/js/timezone-js/Jakefile b/js/timezone-js/Jakefile new file mode 100644 index 0000000..9561690 --- /dev/null +++ b/js/timezone-js/Jakefile @@ -0,0 +1,87 @@ +var fs = require('fs') + , path = require('path'); + +namespace('test', function () { + + desc('Sets up tests by downloading the timezone data.'); + task('init', ['updateTzData'], function () { + complete(); + }, {async: true}); + + task('clobberTzData', function () { + console.log('Removing old timezone data.'); + jake.rmRf('lib/tz'); + }); + + desc('Downloads the newest timezone data.'); + task('updateTzData', ['clobberTzData'], function () { + var cmds = [ + 'echo "Downloading new timezone data ..."' + , 'curl ftp://ftp.iana.org/tz/tzdata-latest.tar.gz ' + + '-o lib/tz/tzdata-latest.tar.gz' + , 'echo "Expanding archive ..."' + , 'tar -xvzf lib/tz/tzdata-latest.tar.gz -C lib/tz' + ]; + jake.mkdirP('lib/tz'); + jake.exec(cmds, function () { + console.log('Retrieved new timezone data'); + console.log('Parsing tz...'); + jake.exec('node src/node-preparse.js lib/tz > lib/all_cities.json', function () { + console.log('Done parsing tz'); + complete(); + }, {printStdout: true, printStderr: true}); + }, {printStdout: true}); + }, {async: true}); + + task('run', function () { + //Comply to 0.8.0 and 0.6.x + var existsSync = fs.existsSync || path.existsSync; + if (!existsSync('lib/tz')) { + fail('No timezone data. Please run "jake test:init".'); + } + jake.exec(['jasmine-node spec'], function () { + complete(); + }, {printStdout: true}); + + }, {async: true}); + + task('cli', ['init', 'run']); + +}); + +desc('Runs the tests.'); +task('test', ['test:run'], function () {}); + +namespace('doc', function () { + task('generate', ['doc:clobber'], function () { + var cmd = 'docco src/date.js'; + console.log('Generating docs ...'); + jake.exec([cmd], function () { + console.log('Done.'); + complete(); + }); + }, {async: true}); + + task('clobber', function () { + var cmd = 'rm -fr ./docs'; + jake.exec([cmd], function () { + console.log('Clobbered old docs.'); + complete(); + }); + }, {async: true}); + +}); + +desc('Generates docs.'); +task('doc', ['doc:generate']); + +var p = new jake.NpmPublishTask('timezone-js', [ + 'Jakefile' +, 'README.md' +, 'package.json' +, 'spec/*' +, 'src/*' +]); + +jake.Task['npm:definePackage'].invoke(); + diff --git a/js/timezone-js/README.md b/js/timezone-js/README.md new file mode 100644 index 0000000..0dc1374 --- /dev/null +++ b/js/timezone-js/README.md @@ -0,0 +1,192 @@ +# TimezoneJS.Date + +[![Build Status](https://secure.travis-ci.org/mde/timezone-js.png)](https://secure.travis-ci.org/mde/timezone-js) + +A timezone-enabled, drop-in replacement for the stock JavaScript Date. The `timezoneJS.Date` object is API-compatible with JS Date, with the same getter and setter methods -- it should work fine in any code that works with normal JavaScript Dates. + +[Mailing list](http://groups.google.com/group/timezone-js) + +## Overview + +The `timezoneJS.Date` object gives you full-blown timezone support, independent from the timezone set on the end-user's machine running the browser. It uses the Olson zoneinfo files for its timezone data. + +The constructor function and setter methods use proxy JavaScript Date objects behind the scenes, so you can use strings like '10/22/2006' with the constructor. You also get the same sensible wraparound behavior with numeric parameters (like setting a value of 14 for the month wraps around to the next March). + +The other significant difference from the built-in JavaScript Date is that `timezoneJS.Date` also has named properties that store the values of year, month, date, etc., so it can be directly serialized to JSON and used for data transfer. + +## Setup + +First you'll need to include the code on your page. Both `timezoneJS.Date`, and the supporting code it needs in `timezoneJS.timezone` are bundled in the `date.js` file in `src` directory. Include the code on your page with a normal JavaScript script include, like so: + +