';
+ },
+
+ /**
+ * Get actual icons from list
+ * @returns {undefined}
+ */
+ _icons: function() {
+ var icons = [];
+
+ if(this.options.value.is_private)
+ {
+ icons.push('');
+ }
+ if(this.options.value.alarm && !jQuery.isEmptyObject(this.options.value.alarm) && !this.options.value.is_private)
+ {
+ icons.push('');
+ }
+ if(this.options.value.participants[egw.user('account_id')] && this.options.value.participants[egw.user('account_id')][0] == 'U')
+ {
+ icons.push('');
+ }
+ return icons;
},
/**
@@ -227,41 +379,49 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM],
}
return result;
},
-
- _edit: function()
- {
- if(this.options.value.recur_type)
- {
- var edit_id = this.options.value.id;
- var edit_date = this.options.value.start;
- var that = this;
- var buttons = [
- {text: this.egw().lang("Edit exception"), id: "exception", class: "ui-priority-primary", "default": true},
- {text: this.egw().lang("Edit series"), id:"series"},
- {text: this.egw().lang("Cancel"), id:"cancel"}
- ];
- et2_dialog.show_dialog(function(_button_id)
- {
- switch(_button_id)
- {
- case 'exception':
- that.egw().open(edit_id, 'calendar', 'edit', {date:edit_date,exception: '1'});
- break;
- case 'series':
- that.egw().open(edit_id, 'calendar', 'edit', {date:edit_date});
- break;
- case 'cancel':
- default:
- break;
- }
- },this.egw().lang("Do you want to edit this event as an exception or the whole series?"),
- this.egw().lang("This event is part of a series"), {}, buttons, et2_dialog.WARNING_MESSAGE);
+ /**
+ * Show the recur prompt for this event
+ *
+ * @param {function} callback
+ */
+ recur_prompt: function(callback)
+ {
+ et2_calendar_event.recur_prompt(this.options.value,callback);
+ },
+
+ /**
+ * Link the actions to the DOM nodes / widget bits.
+ *
+ * @param {object} actions {ID: {attributes..}+} map of egw action information
+ */
+ _link_actions: function(actions)
+ {
+ // Get the top level element - timegrid or so
+ var objectManager = egw_getAppObjectManager(true).getObjectById(this._parent._parent._parent.id) || egw_getAppObjectManager(true);
+ var widget_object = objectManager.getObjectById('calendar::'+this.id);
+ if (widget_object == null) {
+ // Add a new container to the object manager which will hold the widget
+ // objects
+ widget_object = objectManager.insertObject(false, new egwActionObject(
+ 'calendar::'+this.id, objectManager, new et2_event_action_object_impl(this,this.getDOMNode()),
+ objectManager.manager.getActionById(this.id) || objectManager.manager
+ ));
}
else
{
- this.egw().open(this.options.value.id, 'calendar','edit');
+ widget_object.setAOI(new et2_event_action_object_impl(this, this.getDOMNode()));
}
+
+ // Delete all old objects
+ widget_object.clear();
+ widget_object.unregisterActions();
+
+ // Go over the widget & add links - this is where we decide which actions are
+ // 'allowed' for this widget at this time
+ var action_links = this._get_action_links(actions);
+ this._parent._parent._init_links_dnd(widget_object.manager,action_links);
+ widget_object.updateActionLinks(action_links);
},
/**
@@ -281,4 +441,122 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM],
},
});
-et2_register_widget(et2_calendar_event, ["calendar-event"]);
\ No newline at end of file
+et2_register_widget(et2_calendar_event, ["calendar-event"]);
+
+// Static class stuff
+/**
+ * Recur prompt
+ * If the event is recurring, asks the user if they want to edit the event as
+ * an exception, or change the whole series. Then the callback is called.
+ *
+ * @param {Object} event_data - Event information
+ * @param {string} event_data.id - Unique ID for the event, possibly with a timestamp
+ * @param {string|Date} event_data.start - Start date/time for the event
+ * @param {number} event_data.recur_type - Recur type, or 0 for a non-recurring event
+ * @param {Function} [callback] - Callback is called with the button (exception, series, single or cancel) and the event data.
+ *
+ * @augments {et2_calendar_event}
+ */
+et2_calendar_event.recur_prompt = function(event_data, callback)
+{
+ var edit_id = event_data.id;
+ var edit_date = event_data.start;
+ var egw = this.egw ? (typeof this.egw == 'function' ? this.egw() : this.egw) : (window.opener || window).egw;
+ var that = this;
+
+ if(typeof callback != 'function')
+ {
+ callback = function(_button_id)
+ {
+ switch(_button_id)
+ {
+ case 'exception':
+ egw.open(edit_id, event_data.app||'calendar', 'edit', {date:edit_date,exception: '1'});
+ break;
+ case 'series':
+ case 'single':
+ egw.open(edit_id, event_data.app||'calendar', 'edit', {date:edit_date});
+ break;
+ case 'cancel':
+ default:
+ break;
+ }
+ };
+ }
+ if(event_data.recur_type)
+ {
+ var buttons = [
+ {text: egw.lang("Edit exception"), id: "exception", class: "ui-priority-primary", "default": true},
+ {text: egw.lang("Edit series"), id:"series"},
+ {text: egw.lang("Cancel"), id:"cancel"}
+ ];
+ et2_dialog.show_dialog(
+ function(button_id) {callback.call(that, button_id, event_data);},
+ (!event_data.is_private ? event_data['title'] : egw.lang('private')) + "\n" +
+ egw.lang("Do you want to edit this event as an exception or the whole series?"),
+ egw.lang("This event is part of a series"), {}, buttons, et2_dialog.QUESTION_MESSAGE
+ );
+ }
+ else
+ {
+ callback.call(this,'single',event_data);
+ }
+};
+
+et2_calendar_event.drag_helper = function(event,ui) {
+ debugger;
+ ui.helper.width(ui.width());
+};
+/**
+* splits the combined status, quantity and role
+*
+* @param {string} status - combined value, O: status letter: U, T, A, R
+* @param {int} [quantity] - quantity
+* @param {string} [role]
+* @return string status U, T, A or R, same as $status parameter on return
+*/
+et2_calendar_event.split_status = function(status,quantity,role)
+{
+ quantity = 1;
+ role = 'REQ-PARTICIPANT';
+ //error_log(__METHOD__.__LINE__.array2string($status));
+ var matches = null;
+ if (typeof status === 'string' && status.length > 1)
+ {
+ matches = status.match(/^.([0-9]*)(.*)$/gi);
+ }
+ if(matches)
+ {
+ if (parseInt(matches[1]) > 0) quantity = parseInt(matches[1]);
+ if (matches[2]) role = matches[2];
+ status = status[0];
+ }
+ else if (status === true)
+ {
+ status = 'U';
+ }
+ return status;
+}
+
+/**
+ * The egw_action system requires an egwActionObjectInterface Interface implementation
+ * to tie actions to DOM nodes. This one can be used by any widget.
+ *
+ * The class extension is different than the widgets
+ *
+ * @param {et2_DOMWidget} widget
+ * @param {Object} node
+ *
+ */
+function et2_event_action_object_impl(widget, node)
+{
+ var aoi = new et2_action_object_impl(widget, node);
+
+// _outerCall may be used to determine, whether the state change has been
+// evoked from the outside and the stateChangeCallback has to be called
+// or not.
+ aoi.doSetState = function(_state, _outerCall) {
+ };
+
+ return aoi;
+};
diff --git a/calendar/js/et2_widget_timegrid.js b/calendar/js/et2_widget_timegrid.js
index aeec449d52..8ffbb48a00 100644
--- a/calendar/js/et2_widget_timegrid.js
+++ b/calendar/js/et2_widget_timegrid.js
@@ -78,6 +78,18 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
description: "Account ID number of the calendar owner, if not the current user"
},
+ "onchange": {
+ "name": "onchange",
+ "type": "js",
+ "default": et2_no_init,
+ "description": "JS code which is executed when the date range changes."
+ },
+ "onevent_change": {
+ "name": "onevent_change",
+ "type": "js",
+ "default": et2_no_init,
+ "description": "JS code which is executed when an event changes."
+ },
height: {
"default": '100%'
}
@@ -128,24 +140,210 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
// date_helper has no parent, so we must explicitly remove it
this.date_helper.destroy();
this.date_helper = null;
+
+ // Stop the invalidate timer
+ if(this.update_timer)
+ {
+ window.clearTimeout(this.update_timer);
+ }
},
doLoadingFinished: function() {
this._super.apply(this, arguments);
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 timegrid = this;
+
+ // Show the current time while dragging
+ var drag_helper = function(event, element,height)
+ {
+ this.dropEnd = timegrid._get_time_from_position(element.getBoundingClientRect().left,
+ element.getBoundingClientRect().top+parseInt(height));
+
+ if (typeof this.dropEnd != 'undefined' && this.dropEnd.length)
+ {
+ this.dropEnd.addClass("drop-hover");
+ var time = jQuery.datepicker.formatTime(
+ egw.preference("timeformat") == 12 ? "h:mmtt" : "HH:mm",
+ {
+ hour: this.dropEnd.attr('data-hour'),
+ minute: this.dropEnd.attr('data-minute'),
+ seconds: 0,
+ timezone: 0
+ },
+ {"ampm": (egw.preference("timeformat") == "12")}
+ );
+ this.innerHTML = '
'+time+'
';
+ }
+ else
+ {
+ this.innerHTML = '';
+ }
+ return this.dropEnd;
+ };
+
+ this.div.on('mouseover', '.calendar_calEvent:not(.ui-resizable):not(.rowNoEdit)', function() {
+ // Load the event
+ timegrid._get_event_info(this);
+ var that = this;
+
+ //Resizable event handler
+ $j(this).resizable
+ ({
+ distance: 10,
+ grid: [10000,timegrid.rowHeight],
+ autoHide: true,
+ handles: 's,se',
+ containment:'parent',
+
+ /**
+ * Triggered when the resizable is created.
+ *
+ * @param {event} event
+ * @param {Object} ui
+ */
+ create:function(event, ui)
+ {
+ var resizeHelper = event.target.getAttribute('data-resize');
+ if (resizeHelper == 'WD' || resizeHelper == 'WDS')
+ {
+ jQuery(this).resizable('destroy');
+ }
+ },
+
+ /**
+ * Triggered at start of resizing a calEvent
+ *
+ * @param {event} event
+ * @param {Object} ui
+ */
+ start:function(event, ui)
+ {
+ this.dropStart = timegrid._get_time_from_position(ui.element[0].getBoundingClientRect().left,ui.element[0].getBoundingClientRect().top).last();
+ this.dropDate = timegrid._get_event_info(this).start;
+ },
+
+ /**
+ * Triggered at the end of resizing the calEvent.
+ *
+ * @param {event} event
+ * @param {Object} ui
+ */
+ stop:function(event, ui)
+ {
+ var e = new jQuery.Event('change');
+ e.originalEvent = event;
+ e.data = {duration: 0};
+ var event_data = timegrid._get_event_info(this);
+ var event_widget = timegrid.getWidgetById(event_data.id);
+
+ var sT = parseInt(this.dropStart.attr('data-hour'))* 60 + parseInt(this.dropStart.attr('data-minute'));
+ if (typeof this.dropEnd != 'undefined' && this.dropEnd.length == 1)
+ {
+ var eT = parseInt(this.dropEnd.attr('data-hour') * 60) + parseInt(this.dropEnd.attr('data-minute'));
+ e.data.duration = ((eT - sT)/60) * 3600;
+
+
+
+ if(event_widget)
+ {
+ event_widget.options.value.duration = e.data.duration;
+ }
+ $j(this).trigger(e);
+
+
+ // That cleared the resize handles, so remove for re-creation...
+ $j(this).resizable('destroy');
+ }
+ // Clear the helper, re-draw
+ event_widget.set_value(event_widget.options.value);
+ },
+
+ /**
+ * Triggered during the resize, on the drag of the resize handler
+ *
+ * @param {event} event
+ * @param {Object} ui
+ */
+ resize:function(event, ui)
+ {
+ // Add 5px to make sure it doesn't land right on the edge of a div
+ drag_helper.call(this,event,ui.element[0],ui.helper.outerHeight()+5);
+ }
+ });
+ });
+
+ // Customize and override some draggable settings
+ this.div.on('dragcreate','.calendar_calEvent:not(.rowNoEdit)', function(event,ui) {
+ $j(this).draggable('option','cursorAt',false);
+ })
+ .on('dragstart', '.calendar_calEvent:not(.rowNoEdit)', function(event,ui) {
+ $j('.calendar_calEvent',ui.helper).width($j(this).width())
+ .height($j(this).outerHeight())
+ .appendTo(ui.helper);
+ })
+ .on('dragstop','.calendar_calEvent:not(.rowNoEdit)', function(event,ui) {
+ var e = new jQuery.Event('change');
+ e.originalEvent = event;
+ e.data = {start: 0};
+ if (typeof this.dropEnd != 'undefined' && this.dropEnd.length >= 1)
+ {
+ var drop_date = this.dropEnd.attr('data-date')||false;
+
+ var eT = parseInt(this.dropEnd.attr('data-hour') * 60) + parseInt(this.dropEnd.attr('data-minute'));
+
+ var event_data = timegrid._get_event_info(this);
+ var event_widget = timegrid.getWidgetById(event_data.id);
+
+ if(event_widget)
+ {
+ event_widget._parent.date_helper.set_year(drop_date.substring(0,4));
+ event_widget._parent.date_helper.set_month(drop_date.substring(4,6));
+ event_widget._parent.date_helper.set_date(drop_date.substring(6,8));
+ event_widget._parent.date_helper.set_hours(this.dropEnd.attr('data-hour'));
+ event_widget._parent.date_helper.set_minutes(this.dropEnd.attr('data-minute'));
+ event_widget.options.value.start = event_widget._parent.date_helper.getValue();
+
+ event_widget.recur_prompt(function(button_id) {
+ //Get infologID if in case if it's an integrated infolog event
+ if (event_data.app === 'infolog')
+ {
+ // If it is an integrated infolog event we need to edit infolog entry
+ egw().json('stylite_infolog_calendar_integration::ajax_moveInfologEvent', [event_data.id, event_widget.options.value.start||false]).sendRequest();
+ }
+ else
+ {
+ //Edit calendar event
+ egw().json('calendar.calendar_uiforms.ajax_moveEvent',[button_id=='series' ? event_data.id : event_data.app_id,event_data.owner, event_widget.options.value.start, timegrid.options.owner||egw.user('account_id')]).sendRequest();
+ }
+ });
+ }
+ }
+ })
+ // As event is dragged, update the time
+ .on('drag', '.calendar_calEvent:not(.rowNoEdit)', function(event,ui) {
+ this.dropEnd = drag_helper.call($j('.calendar_calEventHeader',ui.helper)[0],event,ui.helper[0],0);
+ $j('.calendar_timeDemo',ui.helper).css('bottom','auto');
+ });
+
// Bind scroll event
// When the user scrolls, we'll move enddate - startdate days
this.div.on('wheel',jQuery.proxy(function(e) {
var direction = e.originalEvent.deltaY > 0 ? 1 : -1;
- this.date_helper.set_value(this.options.end_date);
+ this.date_helper.set_value(this.options.end_date || this.options.start_date);
var end = this.date_helper.get_time();
this.date_helper.set_value(this.options.start_date);
var start = this.date_helper.get_time();
- var delta = 1000 * 60 * 60 * 24 + (end - start);// / (1000 * 60 * 60 * 24));
+ var delta = 1000 * 60 * 60 * 24 + Math.max(0,end - start);
// TODO - actually fetch new data
this.set_start_date(new Date(start + (delta * direction )));
@@ -153,15 +351,6 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
e.preventDefault();
return false;
- },this))
- // Bind context event to create actionobjects as needed
- // TODO: Do it like this, or the normal way?
- .on('contextmenu', jQuery.proxy(function(e) {
- if(this.days.has(e.target).length)
- {
- var event = this._get_event_info(e.originalEvent.target);
- this._link_event(event);
- }
},this));
return true;
@@ -173,10 +362,12 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
* the days.
* The whole grid is not regenerated because times aren't expected to change,
* just the days.
- *
+ *
+ * @param {boolean} trigger=false Trigger an event once things are done.
+ * Waiting until invalidate completes prevents 2 updates when changing the date range.
* @returns {undefined}
*/
- invalidate: function() {
+ invalidate: function(trigger) {
// Reset the list of days
this.day_list = [];
@@ -185,12 +376,49 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
if(this.update_timer === null)
{
this.update_timer = window.setTimeout(jQuery.proxy(function() {
- this.update_timer = null;
- this._drawDays();
- },this),ET2_GRID_INVALIDATE_TIMEOUT);
+ this.widget.update_timer = null;
+
+ // Update actions
+ if(this._actionManager)
+ {
+ this._link_actions(this._actionManager.children);
+ }
+
+ this.widget._drawDays();
+ if(this.trigger)
+ {
+ this.widget.change();
+ }
+ },{widget:this,"trigger":trigger}),ET2_GRID_INVALIDATE_TIMEOUT);
}
},
+ detachFromDOM: function() {
+ // Remove the binding to the change handler
+ $j(this.div).off("change.et2_calendar_timegrid");
+
+ this._super.apply(this, arguments);
+ },
+
+ attachToDOM: function() {
+ this._super.apply(this, arguments);
+
+ // Add the binding for the event change handler
+ $j(this.div).on("change.et2_calendar_timegrid", '.calendar_calEvent', this, function(e) {
+ // Make sure function gets a reference to the widget
+ var args = Array.prototype.slice.call(arguments);
+ if(args.indexOf(this) == -1) args.push(this);
+
+ return e.data.event_change.apply(e.data, args);
+ });
+
+ // Add the binding for the change handler
+ $j(this.div).on("change.et2_calendar_timegrid", '*:not(.calendar_calEvent)', this, function(e) {
+ return e.data.change.call(e.data, e, this);
+ });
+
+ },
+
getDOMNode: function(_sender) {
if(_sender === this || !_sender)
{
@@ -216,7 +444,7 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
// Draw in the vertical - the days
this.div.append(this.days);
- this._drawDays();
+ this.invalidate();
},
/**
@@ -229,7 +457,8 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
var granularity = this.options.granularity;
var totalDisplayMinutes = wd_end - wd_start;
var rowsToDisplay = (totalDisplayMinutes/granularity)+2+2*this.options.extra_rows;
- var rowHeight = (100/rowsToDisplay).toFixed(1);
+ var rowHeight = (100/rowsToDisplay).toFixed(1);
+ this.rowHeight = this.div.height() / rowsToDisplay;
// ensure a minimum height of each row
if (this.options.height < (rowsToDisplay+1) * 12)
@@ -266,7 +495,7 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
);
var time_label = (typeof show[granularity] === 'undefined' ? t % 60 === 0 : show[granularity].indexOf(t % 60) !== -1) ? time : '';
- html += '
'+time_label+"
\n";
+ html += '
'+time_label+"
\n";
}
this.div.append(html);
},
@@ -311,22 +540,28 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
}
// Create / update day widgets with dates and data, if available
+ // TODO: need data doesn't take category & other filters into account
+ var need_data = true;
for(var i = 0; i < this.day_list.length; i++)
{
day = this.day_widgets[i];
// Set the date, and pass any data we have
+ if(typeof this.value[this.day_list[i]] === 'undefined') need_data = true;
+ if(day.options.owner != this.options.owner) need_data = true;
+
day.set_date(this.day_list[i], this.value[this.day_list[i]] || false);
+ day.set_owner(this.options.owner);
day.set_id(this.day_list[i]);
day.set_width((100/this.day_list.length).toFixed(2) + '%');
// Position
$j(day.getDOMNode()).css('left', ((100/this.day_list.length).toFixed(2) * i) + '%');
}
-
- // Update actions
- if(this._actionManager)
+
+ // Fetch any needed data
+ if(need_data)
{
- this._link_actions(this._actionManager.children);
+ this._fetch_data();
}
// TODO: Figure out how to do this with detached nodes
@@ -365,7 +600,7 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
do
{
- if(show_weekend || !show_weekend && [0,6].indexOf(this.date_helper.date.getUTCDay()) === -1)
+ if(show_weekend || !show_weekend && [0,6].indexOf(this.date_helper.date.getUTCDay()) === -1 || end_date == start_date)
{
day_list.push(''+this.date_helper.get_year() + sprintf('%02d',this.date_helper.get_month()) + sprintf('%02d',this.date_helper.get_date()));
}
@@ -385,45 +620,313 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
*/
_link_actions: function(actions)
{
- this._super.apply(this, arguments);
-
- // Get the top level element for the tree
+ // Get the parent? Might be a grid row, might not. Either way, it is
+ // just a container with no valid actions
var objectManager = egw_getAppObjectManager(true);
- var widget_object = objectManager.getObjectById(this.id);
+ var parent = objectManager.getObjectById(this._parent.id);
+ if(!parent) return;
+
+ for(var i = 0; i < parent.children.length; i++)
+ {
+ var parent_finder = jQuery(this.div, parent.children[i].iface.doGetDOMNode());
+ if(parent_finder.length > 0)
+ {
+ parent = parent.children[i];
+ break;
+ }
+ }
+ return;
+// Ug.
+ // This binds into the egw action system. Most user interactions (drag to move, resize)
+ // are handled internally using jQuery directly.
+ var widget_object = parent.getObjectById(this.id);
+ var aoi = new et2_action_object_impl(this,this.getDOMNode());
+ aoi.doTriggerEvent = function(_event, _data) {
- // Time grid is just a container
- widget_object.flags = EGW_AO_FLAG_IS_CONTAINER;
- },
+ // Determine target node
+ var event = _data.event || false;
+ if(!event) return;
+ var nodes = $j('.calendar_calAddEvent[data-hour]',this.doGetDOMNode()).filter(function() {
+ var offset = $j(this).offset();
+ var range={x:[offset.left,offset.left+$j(this).outerWidth()],y:[offset.top,offset.top+$j(this).outerHeight()]};
+ return(event.pageX >=range.x[0] && event.pageX <= range.x[1]) && (event.pageY >= range.y[0] && event.pageY <= range.y[1]);
+ });
- /**
- * Bind a single event as needed to the action system.
- *
- * @param {Object} event
- */
- _link_event: function(event)
- {
- if(!event || !event.app_id) return;
+ switch(_event)
+ {
+ case EGW_AI_DRAG_OVER:
+ // Highlight target time, and display time in helper
+ if(nodes.length)
+ {
+ // Highlight the destination time
+ $j('[data-date]',this.doGetDOMNode()).removeClass("ui-state-active");
+ nodes.addClass('ui-state-active');
+
+ // Update the helper with the actual time
+ var time = jQuery.datepicker.formatTime(
+ egw.preference("timeformat") == 12 ? "h:mmtt" : "HH:mm",
+ {
+ hour: nodes.attr('data-hour'),
+ minute: nodes.attr('data-minute'),
+ seconds: 0,
+ timezone: 0
+ },
+ {"ampm": (egw.preference("timeformat") == "12")}
+ );
+
+ _data.ui.helper[0].innerHTML = '
'+time+'
';
+ if(_data.ui.draggable)
+ {
+ _data.ui.draggable
+ .off('.et2_timegrid')
+ .on('drag.et2_timegrid',jQuery.proxy(function(event, ui) {this.doTriggerEvent(EGW_AI_DRAG_OVER,{event:event,ui:ui});},this))
+ _data.ui.helper.css('width', _data.ui.draggable.width()+'px')
+ .css('height', _data.ui.draggable.height()+'px');
+ }
+ }
+ break;
+ case EGW_AI_DRAG_OUT:
+ // Reset
+ $j('[data-date]',this.doGetDOMNode()).removeClass("ui-state-active");
+ _data.ui.draggable.off('.et2_timegrid');
+ $j('.calendar_d-n-d_timeCounter',_data.ui.helper[0]).remove();
+ break;
+ }
+ };
+ if (widget_object == null) {
+ // Add a new container to the object manager which will hold the widget
+ // objects
+ widget_object = parent.insertObject(false, new egwActionObject(
+ this.id, parent, aoi,
+ parent.manager.getActionById(this.id) || parent.manager
+ ));
+ }
+ else
+ {
+ widget_object.setAOI(aoi);
+ }
+
+ // Delete all old objects
+ widget_object.clear();
+ widget_object.unregisterActions();
// Go over the widget & add links - this is where we decide which actions are
// 'allowed' for this widget at this time
- var objectManager = egw_getObjectManager(this.id,false);
- if(objectManager == null)
+ var action_links = this._get_action_links(actions);
+
+
+ this._init_links_dnd(widget_object.manager, action_links);
+
+ widget_object.updateActionLinks(action_links);
+ },
+
+ /**
+ * Automatically add dnd support for linking
+ */
+ _init_links_dnd: function(mgr,actionLinks) {
+ var self = this;
+
+ var drop_action = mgr.getActionById('egw_link_drop');
+ var drag_action = mgr.getActionById('egw_link_drag');
+
+ // Check if this app supports linking
+ if(!egw.link_get_registry(this.dataStorePrefix || this.egw().appName, 'query') ||
+ egw.link_get_registry(this.dataStorePrefix || this.egw().appName, 'title'))
{
- // No actions set up
+ if(drop_action)
+ {
+ drop_action.remove();
+ if(actionLinks.indexOf(drop_action.id) >= 0)
+ {
+ actionLinks.splice(actionLinks.indexOf(drop_action.id),1);
+ }
+ }
+ if(drag_action)
+ {
+ drag_action.remove();
+ if(actionLinks.indexOf(drag_action.id) >= 0)
+ {
+ actionLinks.splice(actionLinks.indexOf(drag_action.id),1);
+ }
+ }
return;
}
-
- var obj = null;
- debugger;
- if(!(obj = objectManager.getObjectById(event.app_id)))
+
+ // Don't re-add
+ if(drop_action == null)
{
- obj = objectManager.addObject(event.app_id, new et2_action_object_impl(this,event.event_node));
- obj.data = event;
- obj.updateActionLinks(objectManager.actionLinks)
+ // Create the drop action that links entries
+ drop_action = mgr.addAction('drop', 'egw_link_drop', egw.lang('Create link'), egw.image('link'), function(action, source, dropped) {
+ // Extract link IDs
+ var links = [];
+ var id = '';
+ for(var i = 0; i < source.length; i++)
+ {
+ if(!source[i].id) continue;
+ id = source[i].id.split('::');
+ links.push({app: id[0] == 'filemanager' ? 'link' : id[0], id: id[1]});
+ }
+ if(!links.length)
+ {
+ return;
+ }
+
+ // Link the entries
+ egw.json(self.egw().getAppName()+".etemplate_widget_link.ajax_link.etemplate",
+ dropped.id.split('::').concat([links]),
+ function(result) {
+ if(result)
+ {
+ this.egw().message('Linked');
+ }
+ },
+ self,
+ true,
+ self
+ ).sendRequest();
+
+ },true);
}
- objectManager.setAllSelected(false);
- obj.setSelected(true);
- objectManager.updateSelectedChildren(obj,true)
+ if(actionLinks.indexOf(drop_action.id) < 0)
+ {
+ actionLinks.push(drop_action.id);
+ }
+ // Accept other links, and files dragged from the filemanager
+ // This does not handle files dragged from the desktop. They are
+ // handled by et2_nextmatch, since it needs DOM stuff
+ if(drop_action.acceptedTypes.indexOf('link') == -1)
+ {
+ drop_action.acceptedTypes.push('link');
+ }
+
+ // Don't re-add
+ if(drag_action == null)
+ {
+ // Create drag action that allows linking
+ drag_action = mgr.addAction('drag', 'egw_link_drag', egw.lang('link'), 'link', function(action, selected) {
+ // Drag helper - list titles. Arbitrarily limited to 10.
+ var helper = $j(document.createElement("div"));
+ for(var i = 0; i < selected.length && i < 10; i++)
+ {
+ var id = selected[i].id.split('::');
+ var span = $j(document.createElement('span')).appendTo(helper);
+ egw.link_title(id[0],id[1], function(title) {
+ this.append(title);
+ this.append(' ');
+ }, span);
+ }
+ // As we wanted to have a general defaul helper interface, we return null here and not using customize helper for links
+ // TODO: Need to decide if we need to create a customized helper interface for links anyway
+ //return helper;
+ return null;
+ },true);
+ }
+ if(actionLinks.indexOf(drag_action.id) < 0)
+ {
+ actionLinks.push(drag_action.id);
+ }
+ drag_action.set_dragType('link');
+ },
+
+ /**
+ * Get all action-links / id's of 1.-level actions from a given action object
+ *
+ * Here we are only interested in drop events.
+ *
+ * @param actions
+ * @returns {Array}
+ */
+ _get_action_links: function(actions)
+ {
+ var action_links = [];
+ // TODO: determine which actions are allowed without an action (empty actions)
+ for(var i in actions)
+ {
+ var action = actions[i];
+ if(action.type == 'drop')
+ {
+ action_links.push(typeof action.id != 'undefined' ? action.id : i);
+ }
+ }
+ return action_links;
+ },
+
+ /**
+ * Use the egw.data system to get data from the calendar list for the
+ * selected time span.
+ *
+ */
+ _fetch_data: function()
+ {
+ this.egw().dataFetch(
+ this.getInstanceManager().etemplate_exec_id,
+ {start: 0, num_rows:0},
+ jQuery.extend({}, app.calendar.state,
+ {
+ get_rows: 'calendar.calendar_uilist.get_rows',
+ row_id:'row_id',
+ startdate:this.options.start_date,
+ enddate:this.options.end_date,
+ col_filter: {participant: this.options.owner},
+ filter:'custom'
+ }),
+ this.id,
+ function(data) {
+ console.log(data);
+ var updated_days = {};
+ for(var i = 0; i < data.order.length && data.total; i++)
+ {
+ var record = this.egw().dataGetUIDdata(data.order[i]);
+ if(record && record.data)
+ {
+ if(typeof updated_days[record.data.date] === 'undefined')
+ {
+ updated_days[record.data.date] = [];
+ }
+ // Copy, to avoid unwanted changes by reference
+ updated_days[record.data.date].push(jQuery.extend({},record.data));
+
+ // Check for multi-day events listed once
+ // Date must stay a string or we might cause problems with nextmatch
+ var dates = {
+ start: typeof record.data.start === 'string' ? record.data.start : record.data.start.toJSON(),
+ end: typeof record.data.end === 'string' ? record.data.end : record.data.end.toJSON(),
+ };
+ if(dates.start.substr(0,10) != dates.end.substr(0,10))
+ {
+ this.date_helper.set_value(record.data.end);
+ var end = this.date_helper.date.getTime();
+ this.date_helper.set_value(record.data.start);
+
+ do
+ {
+ var expanded_date = ''+this.date_helper.get_year() + sprintf('%02d',this.date_helper.get_month()) + sprintf('%02d',this.date_helper.get_date());
+ if(typeof(updated_days[expanded_date]) == 'undefined')
+ {
+ updated_days[expanded_date] = [];
+ }
+ if(record.data.date !== expanded_date)
+ {
+ // Copy, to avoid unwanted changes by reference
+ updated_days[expanded_date].push(jQuery.extend({},record.data));
+ }
+ this.date_helper.set_date(this.date_helper.get_date()+1);
+ }
+ // Limit it to 14 days to avoid infinite loops in case something is mis-set,
+ // though the limit is more based on how wide the screen is
+ while(end >= this.date_helper.date.getTime() && i <= 14)
+ }
+ }
+ }
+ for(var i = 0; i < this.day_list.length; i++)
+ {
+ var day = this.day_widgets[i];
+ day.set_date(this.day_list[i], updated_days[this.day_list[i]]||[], true);
+
+ this.value[this.day_list[i]] = updated_days[this.day_list[i]];
+ }
+ }, this,null
+ );
},
/**
@@ -442,15 +945,37 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
{
if(typeof events !== 'object') return false;
+ var use_days_sent = true;
+
if(events.owner)
{
this.set_owner(events.owner);
delete events.owner;
}
- this.value = events;
- var day_list = Object.keys(events);
- this.set_start_date(day_list[0]);
- this.set_end_date(day_list[day_list.length-1]);
+ if(events.start_date)
+ {
+ this.set_start_date(events.start_date);
+ delete events.start_date;
+ use_days_sent = false;
+ }
+ if(events.end_date)
+ {
+ this.set_end_date(events.end_date);
+ delete events.end_date;
+ use_days_sent = false;
+ }
+
+ this.value = events || {};
+
+ if(use_days_sent)
+ {
+ var day_list = Object.keys(events);
+ if(day_list.length)
+ {
+ this.set_start_date(day_list[0]);
+ this.set_end_date(day_list[day_list.length-1]);
+ }
+ }
// Reset and calculate instead of just use the keys so we can get the weekend preference
this.day_list = [];
@@ -464,6 +989,11 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
*/
set_start_date: function(new_date)
{
+ if(!new_date || new_date === null)
+ {
+ throw exception('Invalid start date. ' + new_date.toString());
+ }
+
// Use date widget's existing functions to deal
if(typeof new_date === "object" || typeof new_date === "string" && new_date.length > 8)
{
@@ -481,7 +1011,7 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
if(old_date !== this.options.start_date && this.isAttached())
{
- this.invalidate();
+ this.invalidate(true);
}
},
@@ -493,6 +1023,10 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
*/
set_end_date: function(new_date)
{
+ if(!new_date || new_date === null)
+ {
+ throw exception('Invalid end date. ' + new_date.toString());
+ }
// Use date widget's existing functions to deal
if(typeof new_date === "object" || typeof new_date === "string" && new_date.length > 8)
{
@@ -510,7 +1044,74 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
if(old_date !== this.options.end_date && this.isAttached())
{
- this.invalidate();
+ this.invalidate(true);
+ }
+ },
+
+ /**
+ * Call change handler, if set
+ */
+ change: function() {
+ if (this.onchange)
+ {
+ if(typeof this.onchange == 'function')
+ {
+ // Make sure function gets a reference to the widget
+ var args = Array.prototype.slice.call(arguments);
+ if(args.indexOf(this) == -1) args.push(this);
+
+ return this.onchange.apply(this, args);
+ } else {
+ return (et2_compileLegacyJS(this.options.onchange, this, _node))();
+ }
+ }
+ },
+
+ /**
+ * Call event change handler, if set
+ */
+ event_change: function(event, dom_node) {
+ if (this.onevent_change)
+ {
+ var event_data = this._get_event_info(dom_node);
+ var event_widget = this.getWidgetById(event_data.id);
+ et2_calendar_event.recur_prompt(event_data, jQuery.proxy(function(button_id, event_data) {
+ // No need to continue
+ if(button_id === 'cancel') return false;
+
+ if(typeof this.onevent_change == 'function')
+ {
+ // Make sure function gets a reference to the widget
+ var args = Array.prototype.slice.call(arguments);
+
+ if(args.indexOf(event_widget) == -1) args.push(event_widget);
+
+ // Put button ID in event
+ event.button_id = button_id;
+
+ return this.onevent_change.apply(this, [event, event_widget, button_id]);
+ } else {
+ return (et2_compileLegacyJS(this.options.onevent_change, event_widget, dom_node))();
+ }
+ },this));
+ }
+ return false;
+ },
+
+ /**
+ * Turn on or off the visibility of weekends
+ *
+ * @param {boolean} weekends
+ */
+ set_show_weekend: function(weekends)
+ {
+ if(this.options.show_weekend !== weekends)
+ {
+ this.options.show_weekend = weekends ? true : false;
+ if(this.isAttached())
+ {
+ this.invalidate();
+ }
}
},
@@ -554,13 +1155,13 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
if(event.id && result && !this.options.disabled && !this.options.readonly)
{
- this._edit_event(event);
+ et2_calendar_event.recur_prompt(event);
return false;
}
return result;
}
- else
+ else if (_ev.target.dataset.date)
{
// Default handler to open a new event at the selected time
this.egw().open(null, 'calendar', 'add', {
@@ -587,43 +1188,28 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
);
},
- _edit_event: function(event)
- {
- if(event.recur_type)
- {
- var edit_id = event.id;
- var edit_date = event.start;
- var that = this;
- var buttons = [
- {text: this.egw().lang("Edit exception"), id: "exception", class: "ui-priority-primary", "default": true},
- {text: this.egw().lang("Edit series"), id:"series"},
- {text: this.egw().lang("Cancel"), id:"cancel"}
- ];
- et2_dialog.show_dialog(function(_button_id)
- {
- switch(_button_id)
- {
- case 'exception':
- that.egw().open(edit_id, 'calendar', 'edit', {date:edit_date,exception: '1'});
- break;
- case 'series':
- that.egw().open(edit_id, 'calendar', 'edit', {date:edit_date});
- break;
- case 'cancel':
-
- default:
- break;
- }
- },this.egw().lang("Do you want to edit this event as an exception or the whole series?"),
- this.egw().lang("This event is part of a series"), {}, buttons, et2_dialog.WARNING_MESSAGE);
- }
- else
- {
- this.egw().open(event.id, event.app||'calendar','edit');
- }
+ /**
+ * Get time from position
+ *
+ * @param {number} x
+ * @param {number} y
+ * @returns {DOMNode[]} time node(s) for the given position
+ */
+ _get_time_from_position: function(x,y) {
+
+ x = Math.round(x);
+ y = Math.round(y);
+ var nodes = $j('.calendar_calAddEvent[data-hour]',this.div).removeClass('drop-hover').filter(function() {
+ var offset = $j(this).offset();
+ var range={x:[offset.left,offset.left+$j(this).outerWidth()],y:[offset.top,offset.top+$j(this).outerHeight()]};
+
+ var i = (x >=range.x[0] && x <= range.x[1]) && (y >= range.y[0] && y <= range.y[1]);
+ return i;
+ }).addClass("drop-hover");
+
+ return nodes;
},
-
-
+
/**
* Set which user owns this. Owner is passed along to the individual
* days.
@@ -633,17 +1219,15 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
*/
set_owner: function(_owner)
{
+ var old = this.options.owner || 0;
+
// Let select-account widget handle value validation
- this.owner.set_value(_owner);
+ this.owner.set_value(typeof _owner == "string" || typeof _owner == "number" ? _owner : jQuery.extend([],_owner));
this.options.owner = _owner;//this.owner.getValue();
-
- for (var i = this._children.length - 1; i >= 0; i--)
+ if(old !== this.options.owner && this.isAttached())
{
- if(typeof this._children[i].set_owner === 'function')
- {
- this._children[i].set_owner(this.options.owner);
- }
+ this.invalidate(true);
}
},
diff --git a/calendar/setup/setup.inc.php b/calendar/setup/setup.inc.php
index 8c8fe6bdaa..02e4478971 100755
--- a/calendar/setup/setup.inc.php
+++ b/calendar/setup/setup.inc.php
@@ -13,7 +13,7 @@ $setup_info['calendar']['name'] = 'calendar';
$setup_info['calendar']['version'] = '14.2.002';
$setup_info['calendar']['app_order'] = 3;
$setup_info['calendar']['enable'] = 1;
-$setup_info['calendar']['index'] = 'calendar.calendar_uiviews.index';
+$setup_info['calendar']['index'] = 'calendar.calendar_uiviews.index&ajax=true';
$setup_info['calendar']['license'] = 'GPL';
$setup_info['calendar']['description'] =
diff --git a/calendar/templates/default/app.css b/calendar/templates/default/app.css
index d8d4a65599..2c0aae6aa4 100644
--- a/calendar/templates/default/app.css
+++ b/calendar/templates/default/app.css
@@ -13,6 +13,23 @@
}
}
+/**
+ * Sidebox
+ */
+#calendar-sidebox_owner {
+ width: 82%;
+}
+#calendar-sidebox_cat_id {
+ width: 86%;
+}
+#calendar-sidebox_buttons tbody {
+ width: 100%;
+}
+
+#calendar-todo {
+ float: right;
+ width: 30%;
+}
/* Header classes */
tr.dialogHeader td, tr.dialogHeader2 td, tr.dialogHeader3 td, tr.dialogHeader4 td,
tr.dialogOperators td,.dialogFooterToolbar {
@@ -280,6 +297,13 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
border-style: solid;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
+ /* It is important there are no CSS transitions, it breaks resize */
+ -webkit-transition:none;
+ -moz-transition: none !important;
+ -o-transition: none !important;
+ -ms-transition: none !important;
+ transition: none !important;
+
/* set via inline style on runtime:
* top: depending on startime
* height: depending on length
diff --git a/calendar/templates/default/sidebox.xet b/calendar/templates/default/sidebox.xet
new file mode 100644
index 0000000000..eb31e0c1d9
--- /dev/null
+++ b/calendar/templates/default/sidebox.xet
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ />
+
+
+
+
diff --git a/calendar/templates/default/todo.xet b/calendar/templates/default/todo.xet
new file mode 100644
index 0000000000..b153ba80c8
--- /dev/null
+++ b/calendar/templates/default/todo.xet
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/calendar/templates/default/view.xet b/calendar/templates/default/view.xet
index 0e6fe26939..192b2e180e 100644
--- a/calendar/templates/default/view.xet
+++ b/calendar/templates/default/view.xet
@@ -18,8 +18,12 @@ Egroupware
-
-
+
+
+
diff --git a/calendar/templates/pixelegg/app.css b/calendar/templates/pixelegg/app.css
index 8757947f50..e749b1ee30 100755
--- a/calendar/templates/pixelegg/app.css
+++ b/calendar/templates/pixelegg/app.css
@@ -11,7 +11,7 @@
* @package calendar
* @version $Id$
*/
-/* $Id: app.css 52434 2015-04-07 13:15:33Z hnategh $ */
+/* $Id: app.css 52715 2015-05-06 19:03:45Z nathangray $ */
/*Media print classes*/
@media print {
.th td,
@@ -26,6 +26,22 @@
border-bottom: 1px solid gray;
}
}
+/**
+ * Sidebox
+ */
+#calendar-sidebox_owner {
+ width: 82%;
+}
+#calendar-sidebox_cat_id {
+ width: 86%;
+}
+#calendar-sidebox_buttons tbody {
+ width: 100%;
+}
+#calendar-todo {
+ float: right;
+ width: 30%;
+}
/* Header classes */
tr.dialogHeader td,
tr.dialogHeader2 td,
@@ -294,6 +310,12 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
border-style: solid;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
+ /* It is important there are no CSS transitions, it breaks resize */
+ -webkit-transition: none;
+ -moz-transition: none !important;
+ -o-transition: none !important;
+ -ms-transition: none !important;
+ transition: none !important;
/* set via inline style on runtime:
* top: depending on startime
* height: depending on length
diff --git a/etemplate/js/et2_widget_date.js b/etemplate/js/et2_widget_date.js
index 9d29fc8af4..fd651e9d66 100644
--- a/etemplate/js/et2_widget_date.js
+++ b/etemplate/js/et2_widget_date.js
@@ -72,6 +72,12 @@ Date: A date object containing the maximum date.\
Number: A number of days from today. For example 2 represents two days from today and -1 represents yesterday.\
String: A string in the user\'s date format, or a relative date. Relative dates must contain value and period pairs; valid periods are "y" for years, "m" for months, "w" for weeks, and "d" for days. For example, "+1m +7d" represents one month and seven days from today.'
},
+ inline: {
+ "name": "Inline",
+ "type": "boolean",
+ "default": false,
+ "description": "Instead of an input field with a popup calendar, the calendar is displayed inline, with no input field"
+ }
},
legacyOptions: ["data_format"],
@@ -95,9 +101,9 @@ String: A string in the user\'s date format, or a relative date. Relative dates
createInputWidget: function() {
- this.span = $j(document.createElement("span")).addClass("et2_date");
+ this.span = $j(document.createElement(this.options.inline ? 'div' : "span")).addClass("et2_date");
- this.input_date = $j(document.createElement("input"));
+ this.input_date = $j(document.createElement(this.options.inline ? "div" : "input"));
if (this.options.blur) this.input_date.attr('placeholder', this.egw().lang(this.options.blur));
this.input_date.addClass("et2_date").attr("type", "text")
.attr("size", 7) // strlen("10:00pm")=7
@@ -495,7 +501,14 @@ String: A string in the user\'s date format, or a relative date. Relative dates
timezone: 0
});
}
- this.input_date.val(_value);
+ if(this.options.inline )
+ {
+ this.input_date.datepicker("setDate",formatDate);
+ }
+ else
+ {
+ this.input_date.val(_value);
+ }
if(this._oldValue !== et2_no_init && old_value != this.getValue())
{
this.change(this.input_date);
diff --git a/etemplate/js/et2_widget_selectAccount.js b/etemplate/js/et2_widget_selectAccount.js
index 303e1acf3d..49fee81750 100644
--- a/etemplate/js/et2_widget_selectAccount.js
+++ b/etemplate/js/et2_widget_selectAccount.js
@@ -326,7 +326,7 @@ var et2_selectAccount = et2_selectbox.extend(
var found = false;
// Not having a value to look up causes an infinite loop
- if(!search[j]) continue;
+ if(!search[j] || search[j] === "0") continue;
// Options are not indexed, so we must look
for(var i = 0; !found && i < this.options.select_options.length; i++)
diff --git a/etemplate/js/et2_widget_taglist.js b/etemplate/js/et2_widget_taglist.js
index 2eeab537eb..fafdd4c6c7 100644
--- a/etemplate/js/et2_widget_taglist.js
+++ b/etemplate/js/et2_widget_taglist.js
@@ -52,14 +52,14 @@ var et2_taglist = et2_selectbox.extend(
"autocomplete_url": {
"name": "Autocomplete source",
"type": "string",
- "default": "etemplate_widget_taglist.ajax_search.etemplate",
+ "default": "home.etemplate_widget_taglist.ajax_search.etemplate",
"description": "Menuaction (app.class.function) for autocomplete data source. Must return actual JSON, and nothing more."
},
"autocomplete_params": {
"name": "Autocomplete parameters",
"type": "any",
"default": {app:"addressbook"},
- "description": "Extra parameters passed to autocomplete URL"
+ "description": "Extra parameters passed to autocomplete URL. It should be a stringified JSON object."
},
allowFreeEntries: {
@@ -136,6 +136,23 @@ var et2_taglist = et2_selectbox.extend(
},
+ transformAttributes: function(_attrs) {
+ this._super.apply(this, arguments);
+
+ // Handle url parameters - they should be an object
+ if(typeof _attrs.autocomplete_params == 'string')
+ {
+ try
+ {
+ _attrs.autocomplete_params = JSON.parse(_attrs.autocomplete_params)
+ }
+ catch (e)
+ {
+ this.egw().debug('warn', 'Invalid autocomplete_params: '+_attrs.autocomplete_params );
+ }
+ }
+ },
+
doLoadingFinished: function() {
this._super.apply(this, arguments);
diff --git a/etemplate/templates/default/etemplate2.css b/etemplate/templates/default/etemplate2.css
index c64e6a448b..7b6287e12d 100644
--- a/etemplate/templates/default/etemplate2.css
+++ b/etemplate/templates/default/etemplate2.css
@@ -928,7 +928,7 @@ table.et2_grid {
/**
* Sortable grid
*/
-table.et2_grid tbody.ui-sortable > tr:not(.th) {
+table.et2_grid tbody.ui-sortable:not(.ui-sortable-disabled) > tr:not(.th) {
cursor: ns-resize;
}
diff --git a/phpgwapi/inc/class.config.inc.php b/phpgwapi/inc/class.config.inc.php
index fc6adbed46..e543ae299a 100755
--- a/phpgwapi/inc/class.config.inc.php
+++ b/phpgwapi/inc/class.config.inc.php
@@ -264,7 +264,7 @@ class config
'site_title','login_logo_file','login_logo_url','login_logo_title','favicon_file',
'markuntranslated','link_list_thumbnail','enabled_spellcheck','debug_minify',
'call_link','call_popup', // addressbook
- 'hide_birthdays'), // calendar
+ 'hide_birthdays','calview_no_consolidate'), // calendar
'projectmanager' => array('hours_per_workday', 'duration_units'),
'manual' => array('manual_remote_egw_url'),
'infolog' => array('status'),
diff --git a/phpgwapi/js/egw_action/egw_action_dragdrop.js b/phpgwapi/js/egw_action/egw_action_dragdrop.js
index 74c0cbc1d4..becdeb82f5 100644
--- a/phpgwapi/js/egw_action/egw_action_dragdrop.js
+++ b/phpgwapi/js/egw_action/egw_action_dragdrop.js
@@ -651,13 +651,13 @@ function egwDropActionImplementation()
// Set cursor back to auto. Seems FF can't handle cursor reversion
$j('body').css({cursor:'auto'});
- _aoi.triggerEvent(EGW_AI_DRAG_OUT);
+ _aoi.triggerEvent(EGW_AI_DRAG_OUT,{event: event,ui:ui});
},
- "over": function() {
- _aoi.triggerEvent(EGW_AI_DRAG_OVER);
+ "over": function(event, ui) {
+ _aoi.triggerEvent(EGW_AI_DRAG_OVER,{event: event,ui:ui});
},
- "out": function() {
- _aoi.triggerEvent(EGW_AI_DRAG_OUT);
+ "out": function(event,ui) {
+ _aoi.triggerEvent(EGW_AI_DRAG_OUT,{event: event,ui:ui});
},
"tolerance": "pointer",
hoverClass: "drop-hover",