diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index 25a95f9e97..e5f104d071 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -203,12 +203,14 @@ class calendar_uiforms extends calendar_ui 'id' => 1, ); } + $duration = isset($_GET['duration']) ? (int)$_GET['duration'] : (int) $this->bo->cal_prefs['defaultlength']*60; + $end = isset($_GET['end']) ? Api\DateTime::to($_GET['end'], 'ts') : $start + $duration; return array( 'participant_types' => $participant_types, 'participants' => $participants, 'owner' => $owner, 'start' => $start, - 'end' => $start + (int) $this->bo->cal_prefs['defaultlength']*60, + 'end' => $end, 'tzid' => $this->bo->common_prefs['tz'], 'priority' => 2, // normal 'public'=> $this->cal_prefs['default_private'] ? 0 : 1, diff --git a/calendar/js/app.js b/calendar/js/app.js index 323a1e44ba..e6796d720c 100644 --- a/calendar/js/app.js +++ b/calendar/js/app.js @@ -65,6 +65,9 @@ app.classes.calendar = (function(){ "use strict"; return AppJS.extend( owner: egw.user('account_id') }, + /** + * These are the keys we keep to set & remember the status, others are discarded + */ states_to_save: ['owner','status_filter','filter','cat_id','view','sortby','planner_view','weekend'], // If you are in one of these views and select a date in the sidebox, the view @@ -527,7 +530,7 @@ app.classes.calendar = (function(){ "use strict"; return AppJS.extend( if(!sortable.sortable('instance')) { sortable.sortable({ - cancel: "#divAppboxHeader, .calendar_calWeekNavHeader, .calendar_plannerHeader", + cancel: "#divAppboxHeader, .calendar_calWeekNavHeader, .calendar_calDayColHeader, .calendar_plannerHeader", handle: '.calendar_calGridHeader', //placeholder: "srotable_cal_wk_ph", axis:"y", diff --git a/calendar/js/et2_widget_planner.js b/calendar/js/et2_widget_planner.js index b04ee766d3..402d515056 100644 --- a/calendar/js/et2_widget_planner.js +++ b/calendar/js/et2_widget_planner.js @@ -129,10 +129,6 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e this._drawGrid(); } - // Actions may be set on a parent, so we need to explicitly get in here - // and get ours - this._link_actions(this.options.actions || this._parent.options.actions || []); - // Automatically bind drag and resize for every event using jQuery directly // - no action system - var planner = this; @@ -263,13 +259,26 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e planner.vertical_bar .html(''+date(egw.preference('timeformat','calendar') == 12 ? 'h:ia' : 'H:i',formatDate)+'') .show(); + + if(planner.drag_create.event && planner.drag_create.parent && planner.drag_create.end) + { + + planner.drag_create.end.date = time.toJSON() + planner._drag_update_event(); + } } else { // No (valid) time, just hide planner.vertical_bar.hide(); } - }); + }) + .on('mousedown', jQuery.proxy(this._mouse_down, this)) + .on('mouseup', jQuery.proxy(this._mouse_up, this)); + + // Actions may be set on a parent, so we need to explicitly get in here + // and get ours + this._link_actions(this.options.actions || this._parent.options.actions || []); // Customize and override some draggable settings this.div.on('dragcreate','.calendar_calEvent', function(event, ui) { @@ -694,12 +703,6 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e this.widget._drawGrid(); - // Update actions - if(this.widget._actionManager) - { - this.widget._link_actions(this.widget._actionManager.children); - } - if(this.trigger) { this.widget.change(); @@ -1742,6 +1745,9 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e { var result = true; + // Drag to create in progress + if(this.drag_create.start !== null) return; + // Is this click in the event stuff, or in the header? if(!this.options.readonly && this.gridHeader.has(_ev.target).length === 0 && !jQuery(_ev.target).hasClass('calendar_plannerRowHeader')) { @@ -1873,6 +1879,59 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e return new Date(this.date_helper.getValue()); }, + /** + * Mousedown handler to support drag to create + * + * @param {jQuery.Event} event + */ + _mouse_down: function(event) + { + // Get time at mouse + if(this.options.group_by === 'month') + { + var time = this._get_time_from_position(event.clientX, event.clientY); + } + else + { + var time = this._get_time_from_position(event.offsetX, event.offsetY); + } + + this.div.css('cursor', 'ew-resize'); + + // Find the correct row so we know the parent + var row = event.target.closest('.calendar_plannerRowWidget'); + var row_widget = null; + for(var i = 0; i < this._children.length && row; i++) + { + if(this._children[i].div[0] === row) + { + this.drag_create.parent = this._children[i]; + break; + } + } + return this._drag_create_start({date: time.toJSON()}); + }, + + /** + * Mouseup handler to support drag to create + * + * @param {jQuery.Event} event + */ + _mouse_up: function(event) + { + // Get time at mouse + if(this.options.group_by === 'month') + { + var time = this._get_time_from_position(event.clientX, event.clientY); + } + else + { + var time = this._get_time_from_position(event.offsetX, event.offsetY); + } + + return this._drag_create_end({date: time.toJSON()}); + }, + /** * Code for implementing et2_IDetachedDOM * diff --git a/calendar/js/et2_widget_timegrid.js b/calendar/js/et2_widget_timegrid.js index 54634f63fa..3d14aee09c 100644 --- a/calendar/js/et2_widget_timegrid.js +++ b/calendar/js/et2_widget_timegrid.js @@ -306,16 +306,43 @@ var et2_calendar_timegrid = (function(){ "use strict"; return et2_calendar_view. .css('top', '').css('left','') .appendTo(ui.helper); ui.helper.width(jQuery(this).width()); + + // Cancel drag to create, we're dragging an existing event + timegrid.drag_create.start = null; }) .on('mousemove', function(event) { timegrid._get_time_from_position(event.clientX, event.clientY); + + if(timegrid.drag_create.event && timegrid.drag_create.parent && timegrid.drag_create.end) + { + var end = jQuery.extend({}, timegrid.gridHover[0].dataset); + if(end.date) + { + timegrid.date_helper.set_year(end.date.substring(0,4)); + timegrid.date_helper.set_month(end.date.substring(4,6)); + timegrid.date_helper.set_date(end.date.substring(6,8)); + if(end.hour) + { + timegrid.date_helper.set_hours(end.hour); + } + if(end.minute) + { + timegrid.date_helper.set_minutes(end.minute); + } + timegrid.drag_create.end.date = timegrid.date_helper.get_value(); + } + timegrid._drag_update_event(); + } }) .on('mouseout', function(event) { if(timegrid.div.has(event.relatedTarget).length === 0) { timegrid.gridHover.hide(); } - }); + }) + .on('mousedown', jQuery.proxy(this._mouse_down, this)) + .on('mouseup', jQuery.proxy(this._mouse_up, this)); + return true; }, @@ -1751,6 +1778,9 @@ var et2_calendar_timegrid = (function(){ "use strict"; return et2_calendar_view. click: function(_ev) { var result = true; + + // Drag to create in progress + if(this.drag_create.start !== null) return; // Is this click in the event stuff, or in the header? if(_ev.target.dataset.id || jQuery(_ev.target).parents('.calendar_calEvent').length) @@ -1830,6 +1860,67 @@ var et2_calendar_timegrid = (function(){ "use strict"; return et2_calendar_view. } }, + /** + * Mousedown handler to support drag to create + * + * @param {jQuery.Event} event + */ + _mouse_down: function(event) + { + var start = jQuery.extend({},this.gridHover[0].dataset); + if(start.date) + { + // Set parent for event + this.drag_create.parent = this.getWidgetById(start.date); + + // Format date + this.date_helper.set_year(start.date.substring(0,4)); + this.date_helper.set_month(start.date.substring(4,6)); + this.date_helper.set_date(start.date.substring(6,8)); + if(start.hour) + { + this.date_helper.set_hours(start.hour); + } + if(start.minute) + { + this.date_helper.set_minutes(start.minute); + } + start.date = this.date_helper.get_value(); + + + this.gridHover.css('cursor', 'ns-resize'); + } + return this._drag_create_start(start); + }, + + /** + * Mouseup handler to support drag to create + * + * @param {jQuery.Event} event + */ + _mouse_up: function(event) + { + var end = jQuery.extend({}, this.gridHover[0].dataset); + if(end.date) + { + this.date_helper.set_year(end.date.substring(0,4)); + this.date_helper.set_month(end.date.substring(4,6)); + this.date_helper.set_date(end.date.substring(6,8)); + if(end.hour) + { + this.date_helper.set_hours(end.hour); + } + if(end.minute) + { + this.date_helper.set_minutes(end.minute); + } + end.date = this.date_helper.get_value(); + } + + this.gridHover.css('cursor', ''); + return this._drag_create_end(end); + }, + /** * Get time from position for drag and drop * @@ -1878,6 +1969,8 @@ var et2_calendar_timegrid = (function(){ "use strict"; return et2_calendar_view. height: $node.css('padding-bottom') }); day = node; + this.gridHover + .attr('data-non_blocking','true'); break; } if($node.hasClass('calendar_calDayCol')) diff --git a/calendar/js/et2_widget_view.js b/calendar/js/et2_widget_view.js index 9854297fd8..978de0788a 100644 --- a/calendar/js/et2_widget_view.js +++ b/calendar/js/et2_widget_view.js @@ -57,6 +57,14 @@ var et2_calendar_view = (function(){ "use strict"; return et2_valueWidget.extend this.loader = jQuery('
'); this.update_timer = null; + + // Used to support dragging on empty space to create an event + this.drag_create = { + start: null, + end: null, + parent: null, + event: null + }; }, destroy: function destroy() { @@ -383,6 +391,160 @@ var et2_calendar_view = (function(){ "use strict"; return et2_valueWidget.extend result.widget_id = 'event_' + widget_id.join(''); } return result; + }, + + /** + * Starting (mousedown) handler to support drag to create + * + * Extending classes need to set this.drag_create.parent, which is the + * parent container (child of extending class) that will directly hold the + * event. + * + * @param {String} start Date string (JSON format) + */ + _drag_create_start: function(start) + { + this.drag_create.start = jQuery.extend({},start); + if(!this.drag_create.start.date) + { + this.drag_create.start = null; + } + this.drag_create.end = start; + + // Clear some stuff, if last time did not complete + if(this.drag_create.event) + { + this.drag_create.event.destroy(); + this.drag_create.event = null; + } + // Wait a bit before adding an "event", it may be just a click + window.setTimeout(jQuery.proxy(function() { + // Create event + this._drag_create_event(); + }, this), 250); + }, + + /** + * Create or update an event used for feedback while dragging on empty space, + * so user can see something is happening + * + * @param {type} start + */ + _drag_create_event: function() + { + if(!this.drag_create.parent || !this.drag_create.start) + { + return; + } + if(!this.drag_create.event) + { + this.date_helper.set_value(this.drag_create.start.date); + var value = jQuery.extend({}, + this.drag_create.start, + this.drag_create.end, + { + start: this.drag_create.start.date, + end: this.drag_create.end && this.drag_create.end.date || this.drag_create.start.date, + date: ""+this.date_helper.get_year()+ + sprintf("%02d",this.date_helper.get_month())+ + sprintf("%02d",this.date_helper.get_date()), + title: '', + description: '', + owner: this.options.owner, + participants: this.options.owner, + app: 'calendar', + whole_day_on_top: this.drag_create.start.whole_day + } + ); + this.drag_create.event = et2_createWidget('calendar-event',{ + id:'event_drag', + value: value + },this.drag_create.parent); + this.drag_create.event._values_check(value); + this.drag_create.event.doLoadingFinished(); + } + + }, + + _drag_update_event: function() + { + if(!this.drag_create.event || !this.drag_create.start || !this.drag_create.end + || !this.drag_create.parent) + { + return; + } + else if (this.drag_create.end) + { + this.drag_create.event.options.value.end = this.drag_create.end.date; + this.drag_create.event._values_check(this.drag_create.event.options.value); + } + this.drag_create.event._update() + this.drag_create.parent.position_event(this.drag_create.event); + }, + + /** + * Ending (mouseup) handler to support drag to create + * + * @param {String} end Date string (JSON format) + */ + _drag_create_end: function(end) + { + this.div.css('cursor',''); + + if(this.drag_create.start && end.date && + JSON.stringify(this.drag_create.start.date) !== JSON.stringify(end.date)) + { + // Drag from start to end, open dialog + var options = { + start: this.drag_create.start.date < end.date ? this.drag_create.start.date : end.date, + end: this.drag_create.start.date < end.date ? end.date : this.drag_create.start.date + }; + + // Whole day needs to go from 00:00 to 23:59 + if(end.whole_day || this.drag_create.start.whole_day) + { + var start = new Date(options.start); + start.setUTCHours(0); + start.setUTCMinutes(0); + options.start = start.toJSON(); + + var end = new Date(options.end); + end.setUTCHours(23); + end.setUTCMinutes(59); + options.end = end.toJSON(); + } + + // Add anything else that was set, but not date + jQuery.extend(options,this.drag_create.start, end); + delete(options.date); + + if (this.options.owner !== app.calendar.state.owner && !options.owner) + { + options.owner = this.options.owner; + } + this.egw().open(null, 'calendar', 'add', options, '_blank'); + + // Wait a bit, having these stops the click + window.setTimeout(jQuery.proxy(function() { + this.drag_create.start = null; + this.drag_create.end = null; + this.drag_create.parent = null; + if(this.drag_create.event) + { + this.drag_create.event.destroy(); + this.drag_create.event = null; + } + },this),100); + + return false; + } + + this.drag_create.start = null; + this.drag_create.end = null; + this.drag_create.parent = null; + this.drag_create.event.destroy(); + this.drag_create.event = null; + return false; } });}).call(this);