Calendar et2 conversion work in progress.

- Slightly more efficient event positioning
This commit is contained in:
Nathan Gray 2015-07-03 17:56:36 +00:00
parent a5b36d48e0
commit 4562b53b1b
5 changed files with 159 additions and 113 deletions

View File

@ -492,7 +492,7 @@ app.classes.calendar = AppJS.extend(
egw().json( egw().json(
'calendar.calendar_uiforms.ajax_moveEvent', 'calendar.calendar_uiforms.ajax_moveEvent',
[widget.id, widget.options.value.owner, widget.options.value.start, widget.options.value.owner, widget.options.value.duration] [widget.id, widget.options.value.owner, widget.options.value.start, widget.options.value.owner, widget.options.value.duration]
).sendRequest(); ).sendRequest(true);
}, },
/** /**

View File

@ -290,69 +290,41 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM],
// Remove all events // Remove all events
while(this._children.length) while(this._children.length)
{ {
this._children[this._children.length-1].free(); var node = this._children[this._children.length-1];
this.removeChild(node);
node.free();
} }
var events = _events || this.getArrayMgr('content').getEntry(this.options.date) || []; var events = _events || this.getArrayMgr('content').getEntry(this.options.date) || [];
// Sort events into minimally-overlapping columns for(var c = 0; c < events.length; c++)
var columns = this._spread_events(events);
for(var c = 0; c < columns.length; c++)
{ {
// Calculate horizontal positioning
var left = Math.ceil(5 + (1.5 * 100 / (this.options.width || 100)));
var width = 98 - left;
if (columns.length !== 1)
{
width = !c ? 70 : 50;
left += c * (100.0-left) / columns.length;
}
if (left + width > 100.0) width = 98.0 - left;
var whole_day_counter = 0;
for(var i = 0; i < columns[c].length; i++)
{
// Calculate vertical positioning
var top = 0;
var height = 0;
if(columns[c][i].whole_day_on_top)
{
top = ((this.title.height()/this.div.height())*100) + this.display_settings.rowHeight*whole_day_counter++;
height = this.display_settings.rowHeight;
}
else
{
top = this._time_to_position(columns[c][i].start_m,whole_day_counter);
height = this._time_to_position(columns[c][i].end_m,whole_day_counter)-top;
}
// Create event // Create event
var event = et2_createWidget('calendar-event',{id:columns[c][i].app_id||columns[c][i].id},this); var event = et2_createWidget('calendar-event',{
id:events[c].app_id||events[c].id,
value: events[c]
},this);
if(this.isInTree()) if(this.isInTree())
{ {
event.doLoadingFinished(); event.doLoadingFinished();
} }
event.set_value(columns[c][i]);
// Copy actions set in parent
event._link_actions(this._parent._parent.options.actions||{}); event._link_actions(this._parent._parent.options.actions||{});
// Position the event
event.div.css('top', top+'%');
event.div.css('height', height+'%');
event.div.css('left', left.toFixed(1)+'%');
event.div.css('width', width.toFixed(1)+'%');
}
} }
// Seperate loop so column sorting finds all children in the right place
for(var c = 0; c < events.length; c++)
{
this._children[c].set_value(events[c]);
}
}, },
/** /**
* Sort a day's events into minimally overlapping columns * Sort a day's events into minimally overlapping columns
* *
* @param {Object[]} events
* @returns {Array[]} Events sorted into columns * @returns {Array[]} Events sorted into columns
*/ */
_spread_events: function(events) _spread_events: function()
{ {
var day_start = this.date.valueOf() / 1000; var day_start = this.date.valueOf() / 1000;
var dst_check = new Date(this.date); var dst_check = new Date(this.date);
@ -367,9 +339,11 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM],
} }
var eventCols = [], col_ends = []; var eventCols = [], col_ends = [];
for(var i = 0; i < events.length; i++) for(var i = 0; i < this._children.length; i++)
{ {
var event = events[i]; var event = this._children[i].options.value || false;
if(!event) continue;
var c = 0; var c = 0;
event['multiday'] = false; event['multiday'] = false;
if(typeof event.start !== 'object') if(typeof event.start !== 'object')
@ -407,11 +381,70 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM],
{ {
eventCols[c] = []; eventCols[c] = [];
} }
eventCols[c].push(event); eventCols[c].push(this._children[i]);
} }
return eventCols; return eventCols;
}, },
/**
* Position the event according to it's time and how this widget is laid
* out.
*
* @param {undefined|Object|et2_calendar_event} event
*/
position_event: function(event)
{
// Sort events into minimally-overlapping columns
var columns = this._spread_events();
for(var c = 0; c < columns.length; c++)
{
// Calculate horizontal positioning
var left = Math.ceil(5 + (1.5 * 100 / (this.options.width || 100)));
var width = 98 - left;
if (columns.length !== 1)
{
width = !c ? 70 : 50;
left += c * (100.0-left) / columns.length;
}
if (left + width > 100.0) width = 98.0 - left;
var whole_day_counter = 0;
for(var i = 0; (columns[c].indexOf(event) >= 0 || !event) && i < columns[c].length; i++)
{
// Calculate vertical positioning
var top = 0;
var height = 0;
if(columns[c][i].options.value.whole_day_on_top)
{
top = ((this.title.height()/this.div.height())*100) + this.display_settings.rowHeight*whole_day_counter++;
height = this.display_settings.rowHeight;
}
else
{
top = this._time_to_position(columns[c][i].options.value.start_m,whole_day_counter);
height = this._time_to_position(columns[c][i].options.value.end_m,whole_day_counter)-top;
}
// Position the event
if(event && columns[c].indexOf(event) >= 0 || !event)
{
columns[c][i].div.css('top', top+'%');
columns[c][i].div.css('height', height+'%');
columns[c][i].div.css('left', left.toFixed(1)+'%');
columns[c][i].div.css('width', width.toFixed(1)+'%');
}
}
// Only wanted to position this event, leave the other columns alone
if(event && columns[c].indexOf(event) >= 0)
{
return;
}
}
},
/** /**
* Calculates the vertical position based on the time * Calculates the vertical position based on the time
* *

View File

@ -25,6 +25,10 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM],
{ {
attributes: { attributes: {
"value": {
type: "any",
default: et2_no_init
},
"onclick": { "onclick": {
"description": "JS code which is executed when the element is clicked. " + "description": "JS code which is executed when the element is clicked. " +
"If no handler is provided, or the handler returns true and the event is not read-only, the " + "If no handler is provided, or the handler returns true and the event is not read-only, the " +
@ -76,8 +80,11 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM],
this._super.apply(this, arguments); this._super.apply(this, arguments);
// Unregister, or we'll continue to be notified... // Unregister, or we'll continue to be notified...
if(this.options.value)
{
var old_app_id = this.options.value.app_id ? this.options.value.app_id : this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : ''); var old_app_id = this.options.value.app_id ? this.options.value.app_id : this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : '');
egw.dataUnregisterUID('calendar::'+old_app_id,false,this); egw.dataUnregisterUID('calendar::'+old_app_id,false,this);
}
}, },
set_value: function(_value) { set_value: function(_value) {
@ -93,30 +100,13 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM],
var app_id = this.options.value.app_id ? this.options.value.app_id : this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : ''); var app_id = this.options.value.app_id ? this.options.value.app_id : this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : '');
egw.dataRegisterUID('calendar::'+app_id, function(event) { egw.dataRegisterUID('calendar::'+app_id, function(event) {
// Copy to avoid changes, which may cause nm problems // Copy to avoid changes, which may cause nm problems
event = jQuery.extend({},event); this.options.value = jQuery.extend({},event);
var list = [event];
// Let parent format any missing data
this._parent._spread_events(list);
// Calculate vertical positioning // Let parent position
// TODO: Maybe move this somewhere common between here & parent? this._parent.position_event(this);
var top = 0;
var height = 0; this._update(this.options.value);
if(event.whole_day_on_top)
{
top = ((this._parent.title.height()/this._parent.div.height())*100) + this._parent.display_settings.rowHeight;
height = this._parent.display_settings.rowHeight;
}
else
{
top = this._parent._time_to_position(event.start_m,0);
height = this._parent._time_to_position(event.end_m,0)-top;
}
// Position the event - horizontal is controlled by parent
this.div.css('top', top+'%');
this.div.css('height', height+'%');
this._update(event);
},this,this.getInstanceManager().execId,this.id); },this,this.getInstanceManager().execId,this.id);
@ -304,16 +294,18 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM],
icons.push('<img src="'+this.egw().image('recur','calendar')+'" title="'+this.egw().lang('recurring event')+'"/>'); icons.push('<img src="'+this.egw().image('recur','calendar')+'" title="'+this.egw().lang('recurring event')+'"/>');
} }
// icons for single user, multiple users or group(s) and resources // icons for single user, multiple users or group(s) and resources
var single = '<img src="'+this.egw().image('single','calendar')+'" title="'+'"/>';
var multiple = '<img src="'+this.egw().image('users','calendar')+'" title="'+'"/>';
for(var uid in this.options.value['participants']) for(var uid in this.options.value['participants'])
{ {
if(Object.keys(this.options.value.participants).length == 1 && !isNaN(uid)) if(Object.keys(this.options.value.participants).length == 1 && !isNaN(uid))
{ {
icons.push('<img src="'+this.egw().image('single','calendar')+'" title="'+'"/>'); icons.push(single);
break; break;
} }
if(!isNaN(uid)) if(!isNaN(uid) && icons.indexOf(multiple) === -1)
{ {
icons.push('<img src="'+this.egw().image('users','calendar')+'" title="'+'"/>'); icons.push(multiple);
} }
/* /*
* TODO: resource icons * TODO: resource icons

View File

@ -171,9 +171,9 @@ var et2_calendar_planner_row = et2_valueWidget.extend([et2_IDetachedDOM],
* *
* If event information is not provided, it will be pulled from the content array. * If event information is not provided, it will be pulled from the content array.
* *
* @param {Object[]} [_events] Array of event information, one per event. * @param {Object[]} [events] Array of event information, one per event.
*/ */
_update_events: function(_events) _update_events: function(events)
{ {
// Remove all events // Remove all events
while(this._children.length) while(this._children.length)
@ -182,7 +182,38 @@ var et2_calendar_planner_row = et2_valueWidget.extend([et2_IDetachedDOM],
this.removeChild(this._children[this._children.length-1]); this.removeChild(this._children[this._children.length-1]);
} }
var rows = this._spread_events(_events); for(var c = 0; c < events.length; c++)
{
// Create event
var event = et2_createWidget('calendar-event',{
id:events[c].app_id||events[c].id,
value: events[c]
},this);
if(this.isInTree())
{
event.doLoadingFinished();
}
// Copy actions set in parent
event._link_actions(this._parent._parent.options.actions||{});
}
// Seperate loop so column sorting finds all children in the right place
for(var c = 0; c < events.length; c++)
{
this._children[c].set_value(events[c]);
}
},
/**
* Position the event according to it's time and how this widget is laid
* out.
*
* @param {undefined|Object|et2_calendar_event} event
*/
position_event: function(event)
{
var rows = this._spread_events();
var row = $j('<div class="calendar_plannerEventRowWidget"></div>').appendTo(this.rows); var row = $j('<div class="calendar_plannerEventRowWidget"></div>').appendTo(this.rows);
var height = rows.length * (parseInt(window.getComputedStyle(row[0]).getPropertyValue("height")) || 20); var height = rows.length * (parseInt(window.getComputedStyle(row[0]).getPropertyValue("height")) || 20);
row.remove(); row.remove();
@ -192,31 +223,17 @@ var et2_calendar_planner_row = et2_valueWidget.extend([et2_IDetachedDOM],
// Calculate vertical positioning // Calculate vertical positioning
var top = c * (100.0 / rows.length); var top = c * (100.0 / rows.length);
for(var i = 0; i < rows[c].length; i++) for(var i = 0; (rows[c].indexOf(event) >=0 || !event) && i < rows[c].length; i++)
{ {
// Calculate horizontal positioning // Calculate horizontal positioning
var left = this._time_to_position(rows[c][i].start); var left = this._time_to_position(rows[c][i].options.value.start);
var width = this._time_to_position(rows[c][i].end)-left; var width = this._time_to_position(rows[c][i].options.value.end)-left;
// Create event
var event = et2_createWidget('calendar-event',{
id:rows[c][i].app_id||rows[c][i].id,
class: 'calendar_plannerEvent'
},this);
if(this.isInTree())
{
event.doLoadingFinished();
}
event.set_value(rows[c][i]);
// TODO
event._link_actions(this._parent.options.actions||{});
// Position the event // Position the event
event.div.css('top', top+'%'); rows[c][i].div.css('top', top+'%');
event.div.css('height', (100/rows.length)+'%'); rows[c][i].div.css('height', (100/rows.length)+'%');
event.div.css('left', left.toFixed(1)+'%'); rows[c][i].div.css('left', left.toFixed(1)+'%');
event.div.css('width', width.toFixed(1)+'%'); rows[c][i].div.css('width', width.toFixed(1)+'%');
} }
} }
if(height) if(height)
@ -228,10 +245,9 @@ var et2_calendar_planner_row = et2_valueWidget.extend([et2_IDetachedDOM],
/** /**
* Sort a day's events into non-overlapping rows * Sort a day's events into non-overlapping rows
* *
* @param {Object[]} events
* @returns {Array[]} Events sorted into rows * @returns {Array[]} Events sorted into rows
*/ */
_spread_events: function(events) _spread_events: function()
{ {
// sorting the events in non-overlapping rows // sorting the events in non-overlapping rows
var rows = []; var rows = [];
@ -240,9 +256,9 @@ var et2_calendar_planner_row = et2_valueWidget.extend([et2_IDetachedDOM],
var start = this.options.start_date; var start = this.options.start_date;
var end = this.options.end_date; var end = this.options.end_date;
for(var n = 0; n < events.length; n++) for(var n = 0; n < this._children.length; n++)
{ {
var event = events[n]; var event = this._children[n].options.value || false;
if(typeof event.start !== 'object') if(typeof event.start !== 'object')
{ {
this.date_helper.set_value(event.start); this.date_helper.set_value(event.start);
@ -282,11 +298,11 @@ var et2_calendar_planner_row = et2_valueWidget.extend([et2_IDetachedDOM],
} }
} }
var event_start = new Date(events[n].start).valueOf(); var event_start = new Date(event.start).valueOf();
for(var row = 0; row_end[row] > event_start; ++row); // find a "free" row (no other event) for(var row = 0; row_end[row] > event_start; ++row); // find a "free" row (no other event)
if(typeof rows[row] === 'undefined') rows[row] = []; if(typeof rows[row] === 'undefined') rows[row] = [];
rows[row].push(events[n]); rows[row].push(this._children[n]);
row_end[row] = new Date(events[n]['end']).valueOf(); row_end[row] = new Date(event['end']).valueOf();
} }
return rows; return rows;
}, },

View File

@ -245,7 +245,8 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
if(event_widget) if(event_widget)
{ {
event_widget.options.value.end_m = event_widget.options.value.duration = e.data.duration; event_widget.options.value.end_m = eT;
event_widget.options.value.duration = e.data.duration;
} }
$j(this).trigger(e); $j(this).trigger(e);
@ -253,7 +254,10 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
$j(this).resizable('destroy'); $j(this).resizable('destroy');
} }
// Clear the helper, re-draw // Clear the helper, re-draw
event_widget.set_value(event_widget.options.value); if(event_widget)
{
event_widget._parent.position_event(event_widget);
}
}, },
/** /**
@ -517,6 +521,7 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
// Create / update day widgets with dates and data, if available // Create / update day widgets with dates and data, if available
// TODO: need data doesn't take category & other filters into account // TODO: need data doesn't take category & other filters into account
// Currently always loading new data, which causes multiple unneeded redraws
var need_data = true; var need_data = true;
for(var i = 0; i < this.day_list.length; i++) for(var i = 0; i < this.day_list.length; i++)
{ {