mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-25 01:13:25 +01:00
Speed improvements for planner view
This commit is contained in:
parent
ae4eeb6cee
commit
6bccec1e0c
@ -71,6 +71,11 @@ var et2_calendar_event = (function(){ "use strict"; return et2_valueWidget.exten
|
||||
.addClass(this.options.class)
|
||||
.css('width',this.options.width)
|
||||
.on('mouseenter', function() {
|
||||
// Bind actions on first mouseover for faster creation
|
||||
if(event._need_actions_linked)
|
||||
{
|
||||
event._copy_parent_actions();
|
||||
}
|
||||
// Tooltip
|
||||
if(!event._tooltipElem)
|
||||
{
|
||||
@ -108,6 +113,8 @@ var et2_calendar_event = (function(){ "use strict"; return et2_valueWidget.exten
|
||||
.appendTo(this.title);
|
||||
|
||||
this.setDOMNode(this.div[0]);
|
||||
|
||||
this._need_actions_linked = false;
|
||||
},
|
||||
|
||||
doLoadingFinished: function() {
|
||||
@ -243,23 +250,7 @@ var et2_calendar_event = (function(){ "use strict"; return et2_valueWidget.exten
|
||||
this._actionObject.id = 'calendar::' + id;
|
||||
}
|
||||
|
||||
// Copy actions set in parent
|
||||
if(!this.options.readonly && !this._parent.options.readonly)
|
||||
{
|
||||
var action_parent = this;
|
||||
while(action_parent != null && !action_parent.options.actions &&
|
||||
!action_parent.instanceOf(et2_container)
|
||||
)
|
||||
{
|
||||
action_parent = action_parent.getParent();
|
||||
}
|
||||
try {
|
||||
this._link_actions(action_parent.options.actions||{});
|
||||
} catch (e) {
|
||||
// something went wrong, but keep quiet about it
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
this._need_actions_linked = true;
|
||||
|
||||
// Make sure category stuff is there
|
||||
// Fake it to use the cache / call - if already there, these will return
|
||||
@ -398,6 +389,9 @@ var et2_calendar_event = (function(){ "use strict"; return et2_valueWidget.exten
|
||||
|
||||
if(this.options.value.whole_day_on_top) return;
|
||||
|
||||
// Skip for planner view, it's always small
|
||||
if(this._parent && this._parent.instanceOf(et2_calendar_planner_row)) return;
|
||||
|
||||
// Pre-calculation reset
|
||||
this.div.removeClass('calendar_calEventSmall');
|
||||
this.body.css('height', 'auto');
|
||||
@ -861,6 +855,32 @@ var et2_calendar_event = (function(){ "use strict"; return et2_valueWidget.exten
|
||||
et2_calendar_event.series_split_prompt(this.options.value,this.options.value.recur_date, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the actions set on the parent, apply them to self
|
||||
*
|
||||
* This can take a while to do, so we try to do it only when needed - on mouseover
|
||||
*/
|
||||
_copy_parent_actions: function()
|
||||
{
|
||||
// Copy actions set in parent
|
||||
if(!this.options.readonly && !this._parent.options.readonly)
|
||||
{
|
||||
var action_parent = this;
|
||||
while(action_parent != null && !action_parent.options.actions &&
|
||||
!action_parent.instanceOf(et2_container)
|
||||
)
|
||||
{
|
||||
action_parent = action_parent.getParent();
|
||||
}
|
||||
try {
|
||||
this._link_actions(action_parent.options.actions||{});
|
||||
this._need_actions_linked = false;
|
||||
} catch (e) {
|
||||
// something went wrong, but keep quiet about it
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Link the actions to the DOM nodes / widget bits.
|
||||
*
|
||||
|
@ -64,6 +64,8 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
}
|
||||
},
|
||||
|
||||
DEFERRED_ROW_TIME: 100,
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -108,6 +110,8 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
this.setDOMNode(this.div[0]);
|
||||
|
||||
this.registeredCallbacks = [];
|
||||
this.cache = {};
|
||||
this._deferred_row_updates = {};
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -133,6 +137,9 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
// - no action system -
|
||||
var planner = this;
|
||||
|
||||
this.cache = {};
|
||||
this._deferred_row_updates = {};
|
||||
|
||||
/**
|
||||
* If user puts the mouse over an event, then we'll set up resizing so
|
||||
* they can adjust the length. Should be a little better on resources
|
||||
@ -502,7 +509,20 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
draw_row: function(sort_key, label, events) {
|
||||
if(['user','both'].indexOf(egw.preference('planner_show_empty_rows','calendar')) !== -1 || events.length)
|
||||
{
|
||||
return this._drawRow(sort_key, label,events,this.options.start_date, this.options.end_date);
|
||||
var row = this._drawRow(sort_key, label,events,this.options.start_date, this.options.end_date);
|
||||
|
||||
// Since the daywise cache is by user, we can tap in here
|
||||
var t = new Date(this.options.start_date);
|
||||
var end = new Date(this.options.end_date);
|
||||
do
|
||||
{
|
||||
var cache_id = app.classes.calendar._daywise_cache_id(t, sort_key);
|
||||
egw.dataRegisterUID(cache_id, row._data_callback, row);
|
||||
|
||||
t.setUTCDate(t.getUTCDate() + 1);
|
||||
}
|
||||
while(t < end);
|
||||
return row;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -767,7 +787,10 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
// Show AJAX loader
|
||||
this.widget.loader.show();
|
||||
|
||||
this.widget.value = this.widget._fetch_data();
|
||||
this.widget.cache = {};
|
||||
this._deferred_row_updates = {};
|
||||
|
||||
this.widget._fetch_data();
|
||||
|
||||
this.widget._drawGrid();
|
||||
|
||||
@ -849,7 +872,7 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
.append(this.grid);
|
||||
this.grid.empty();
|
||||
|
||||
var grouper = this.groupers[isNaN(this.options.group_by) ? this.options.group_by : 'category'];
|
||||
var grouper = this.grouper;
|
||||
if(!grouper) return;
|
||||
|
||||
// Headers
|
||||
@ -903,6 +926,14 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
{
|
||||
this.gridHeader.css('margin-right', (this.rows.width() - this.rows.children().last().width()) + 'px');
|
||||
}
|
||||
// Add actual events
|
||||
for(var key in this._deferred_row_updates)
|
||||
{
|
||||
window.clearTimeout(key);
|
||||
}
|
||||
window.setTimeout(jQuery.proxy(function() {
|
||||
this._deferred_row_update();
|
||||
}, this ),this.DEFERRED_ROW_TIME)
|
||||
this.value = [];
|
||||
},
|
||||
|
||||
@ -932,11 +963,6 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
row.doLoadingFinished();
|
||||
}
|
||||
|
||||
// Add actual events
|
||||
window.setTimeout(jQuery.proxy(function() {
|
||||
this.row._update_events(this.events);
|
||||
}, {row: row, events: events} ),0)
|
||||
|
||||
return row;
|
||||
},
|
||||
|
||||
@ -1720,76 +1746,167 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
var end = new Date(this.options.end_date);
|
||||
do
|
||||
{
|
||||
// Cache is by date (and owner, if seperate)
|
||||
var date = t.getUTCFullYear() + sprintf('%02d',t.getUTCMonth()+1) + sprintf('%02d',t.getUTCDate());
|
||||
var cache_id = app.classes.calendar._daywise_cache_id(date, this.options.owner);
|
||||
|
||||
if(egw.dataHasUID(cache_id))
|
||||
{
|
||||
var c = egw.dataGetUIDdata(cache_id);
|
||||
if(c.data && c.data !== null)
|
||||
{
|
||||
// There is data, pass it along now
|
||||
for(var j = 0; j < c.data.length; j++)
|
||||
{
|
||||
if(last_data.indexOf(c.data[j]) === -1 && egw.dataHasUID('calendar::'+c.data[j]))
|
||||
{
|
||||
value.push(egw.dataGetUIDdata('calendar::'+c.data[j]).data);
|
||||
}
|
||||
}
|
||||
last_data = c.data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fetch = true;
|
||||
// Assume it's empty, if there is data it will be filled later
|
||||
egw.dataStoreUID(cache_id, []);
|
||||
}
|
||||
this.registeredCallbacks.push(cache_id);
|
||||
egw.dataRegisterUID(cache_id, function(data) {
|
||||
if(data && data.length)
|
||||
{
|
||||
// If displaying by category, we need the infolog (or other app) categories too
|
||||
var im = this.getInstanceManager();
|
||||
for(var i = 0; i < data.length && this.options.group_by == 'category'; i++)
|
||||
{
|
||||
var event = egw.dataGetUIDdata('calendar::'+data[i]);
|
||||
if(event && event.data && event.data.app)
|
||||
{
|
||||
// Fake it to use the cache / call
|
||||
et2_selectbox.cat_options({
|
||||
_type:'select-cat',
|
||||
getInstanceManager: function() {return im;}
|
||||
}, {application:event.data.app||'calendar'});
|
||||
|
||||
// Get CSS too
|
||||
egw.includeCSS('/api/categories.php?app='+event.data.app);
|
||||
}
|
||||
}
|
||||
|
||||
this.invalidate(false);
|
||||
}
|
||||
}, this, this.getInstanceManager().execId,this.id);
|
||||
value = value.concat(this._cache_register(t, this.options.owner, last_data));
|
||||
|
||||
t.setUTCDate(t.getUTCDate() + 1);
|
||||
}
|
||||
while(t < end);
|
||||
// Need to get some more from the server
|
||||
if(fetch && app.calendar)
|
||||
{
|
||||
app.calendar._fetch_data({
|
||||
first: this.options.start_date,
|
||||
last: this.options.end_date,
|
||||
owner: this.options.owner,
|
||||
filter: this.options.filter
|
||||
}, this.getInstanceManager());
|
||||
}
|
||||
|
||||
this.doInvalidate = true;
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Deal with registering for data cache
|
||||
*
|
||||
* @param Date t
|
||||
* @param String owner Calendar owner
|
||||
*/
|
||||
_cache_register: function _cache_register(t, owner, last_data)
|
||||
{
|
||||
// Cache is by date (and owner, if seperate)
|
||||
var date = t.getUTCFullYear() + sprintf('%02d',t.getUTCMonth()+1) + sprintf('%02d',t.getUTCDate());
|
||||
var cache_id = app.classes.calendar._daywise_cache_id(date, owner);
|
||||
var value = [];
|
||||
|
||||
if(egw.dataHasUID(cache_id))
|
||||
{
|
||||
var c = egw.dataGetUIDdata(cache_id);
|
||||
if(c.data && c.data !== null)
|
||||
{
|
||||
// There is data, pass it along now
|
||||
for(var j = 0; j < c.data.length; j++)
|
||||
{
|
||||
if(last_data.indexOf(c.data[j]) === -1 && egw.dataHasUID('calendar::'+c.data[j]))
|
||||
{
|
||||
value.push(egw.dataGetUIDdata('calendar::'+c.data[j]).data);
|
||||
}
|
||||
}
|
||||
last_data = c.data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fetch = true;
|
||||
// Assume it's empty, if there is data it will be filled later
|
||||
egw.dataStoreUID(cache_id, []);
|
||||
}
|
||||
this.registeredCallbacks.push(cache_id);
|
||||
|
||||
egw.dataRegisterUID(cache_id, function(data) {
|
||||
|
||||
if(data && data.length)
|
||||
{
|
||||
var invalidate = true;
|
||||
|
||||
// Try to determine rows interested
|
||||
var labels = [];
|
||||
var events = {};
|
||||
if(this.grouper)
|
||||
{
|
||||
labels = this.grouper.row_labels.call(this);
|
||||
invalidate = false;
|
||||
}
|
||||
|
||||
var im = this.getInstanceManager();
|
||||
for(var i = 0; i < data.length; i++)
|
||||
{
|
||||
var event = egw.dataGetUIDdata('calendar::'+data[i]);
|
||||
|
||||
// Try to determine rows interested
|
||||
if(event && event.data && this.grouper)
|
||||
{
|
||||
this.grouper.group.call(this, labels, events, event.data);
|
||||
}
|
||||
if(Object.keys(events).length > 0 )
|
||||
{
|
||||
for(var label_id in events)
|
||||
{
|
||||
var id = ""+labels[label_id].id;
|
||||
if(typeof this.cache[id] === 'undefined')
|
||||
{
|
||||
this.cache[id] = [];
|
||||
}
|
||||
if(this.cache[id].indexOf(event.data.row_id) === -1 && (
|
||||
event.data.participants[id] && this.options.group_by === 'user' ||
|
||||
event.data.category === id && this.options.group_by === 'category'
|
||||
))
|
||||
{
|
||||
this.cache[id].push(event.data.row_id);
|
||||
}
|
||||
if (this._deferred_row_updates[id])
|
||||
{
|
||||
window.clearTimeout(this._deferred_row_updates[id]);
|
||||
}
|
||||
this._deferred_row_updates[id] = window.setTimeout(jQuery.proxy(this._deferred_row_update,this,id),this.DEFERRED_ROW_TIME);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Could be an event no row is interested in, could be a problem.
|
||||
// Just redraw everything
|
||||
invalidate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If displaying by category, we need the infolog (or other app) categories too
|
||||
if(event && event.data && event.data.app && this.options.group_by == 'category')
|
||||
{
|
||||
// Fake it to use the cache / call
|
||||
et2_selectbox.cat_options({
|
||||
_type:'select-cat',
|
||||
getInstanceManager: function() {return im;}
|
||||
}, {application:event.data.app||'calendar'});
|
||||
|
||||
// Get CSS too
|
||||
egw.includeCSS('/api/categories.php?app='+event.data.app);
|
||||
}
|
||||
}
|
||||
|
||||
if(invalidate)
|
||||
{
|
||||
this.invalidate(false);
|
||||
}
|
||||
}
|
||||
}, this, this.getInstanceManager().execId,this.id);
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Because users may be participants in various events and the time it takes
|
||||
* to create many events, we don't want to update a row too soon - we may have
|
||||
* to re-draw it if we find the user / category in another event. Pagination
|
||||
* makes this worse. We wait a bit before updating the row to avoid
|
||||
* having to re-draw it multiple times.
|
||||
*
|
||||
* @param {type} id
|
||||
* @returns {undefined}
|
||||
*/
|
||||
_deferred_row_update: function(id) {
|
||||
// Something's in progress, skip
|
||||
if(!this.doInvalidate) return;
|
||||
|
||||
var id_list = typeof id === 'undefined' ? Object.keys(this.cache) : [id];
|
||||
for(var i = 0; i < id_list.length; i++)
|
||||
{
|
||||
var cache_id = id_list[i];
|
||||
var row = this.getWidgetById('planner_row_'+cache_id);
|
||||
|
||||
window.clearTimeout(this._deferred_row_updates[cache_id]);
|
||||
delete this._deferred_row_updates[cache_id];
|
||||
|
||||
if(row)
|
||||
{
|
||||
row._data_callback(this.cache[cache_id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Provide specific data to be displayed.
|
||||
* This is a way to set start and end dates, owner and event data in once call.
|
||||
@ -1802,7 +1919,7 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
* Days should be in order.
|
||||
*
|
||||
*/
|
||||
set_value: function(events)
|
||||
set_value: function set_value(events)
|
||||
{
|
||||
if(typeof events !== 'object') return false;
|
||||
|
||||
@ -1849,7 +1966,7 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
* @param {string|number} group_by 'user', 'month', or an integer category ID
|
||||
* @returns {undefined}
|
||||
*/
|
||||
set_group_by: function(group_by)
|
||||
set_group_by: function set_group_by(group_by)
|
||||
{
|
||||
if(isNaN(group_by) && typeof this.groupers[group_by] === 'undefined')
|
||||
{
|
||||
@ -1858,6 +1975,8 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
var old = this.options.group_by;
|
||||
this.options.group_by = ''+group_by;
|
||||
|
||||
this.grouper = this.groupers[isNaN(this.options.group_by) ? this.options.group_by : 'category'];
|
||||
|
||||
if(old !== this.options.group_by && this.isAttached())
|
||||
{
|
||||
this.invalidate(true);
|
||||
@ -1869,7 +1988,7 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
|
||||
*
|
||||
* @param {boolean} weekends
|
||||
*/
|
||||
set_show_weekend: function(weekends)
|
||||
set_show_weekend: function set_show_weekend(weekends)
|
||||
{
|
||||
weekends = weekends ? true : false;
|
||||
if(this.options.show_weekend !== weekends)
|
||||
|
@ -67,7 +67,7 @@ var et2_calendar_planner_row = (function(){ "use strict"; return et2_valueWidget
|
||||
this.set_end_date(this.options.end_date);
|
||||
|
||||
this._cached_rows = [];
|
||||
|
||||
this._row_height = 20;
|
||||
},
|
||||
|
||||
doLoadingFinished: function() {
|
||||
@ -114,7 +114,6 @@ var et2_calendar_planner_row = (function(){ "use strict"; return et2_valueWidget
|
||||
var parent = objectManager.getObjectById(this.id,1) || objectManager.getObjectById(this._parent.id,1) || objectManager;
|
||||
if(!parent)
|
||||
{
|
||||
debugger;
|
||||
egw.debug('error','No parent objectManager found');
|
||||
return;
|
||||
}
|
||||
@ -436,6 +435,39 @@ var et2_calendar_planner_row = (function(){ "use strict"; return et2_valueWidget
|
||||
return content;
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback used when the daywise data changes
|
||||
*
|
||||
* Events should update themselves when their data changes, here we are
|
||||
* dealing with a change in which events are displayed on this row.
|
||||
*
|
||||
* @param {String[]} event_ids
|
||||
* @returns {undefined}
|
||||
*/
|
||||
_data_callback: function(event_ids) {
|
||||
var events = [];
|
||||
if(event_ids == null || typeof event_ids.length == 'undefined') event_ids = [];
|
||||
for(var i = 0; i < event_ids.length; i++)
|
||||
{
|
||||
var event = egw.dataGetUIDdata('calendar::'+event_ids[i]);
|
||||
event = event && event.data || false;
|
||||
if(event && event.date)
|
||||
{
|
||||
events.push(event);
|
||||
}
|
||||
else if (event)
|
||||
{
|
||||
// Got an ID that doesn't belong
|
||||
event_ids.splice(i--,1);
|
||||
}
|
||||
}
|
||||
if(!this._parent.disabled)
|
||||
{
|
||||
this.resize();
|
||||
this._update_events(events);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the event data for this day and create event widgets for each.
|
||||
*
|
||||
@ -446,10 +478,11 @@ var et2_calendar_planner_row = (function(){ "use strict"; return et2_valueWidget
|
||||
_update_events: function(events)
|
||||
{
|
||||
// Remove all events
|
||||
while(this._children.length)
|
||||
while(this._children.length > 0)
|
||||
{
|
||||
this._children[this._children.length-1].free();
|
||||
this.removeChild(this._children[this._children.length-1]);
|
||||
var node = this._children[this._children.length-1];
|
||||
this.removeChild(node);
|
||||
node.free();
|
||||
}
|
||||
this._cached_rows = [];
|
||||
|
||||
@ -483,10 +516,8 @@ var et2_calendar_planner_row = (function(){ "use strict"; return et2_valueWidget
|
||||
position_event: function(event)
|
||||
{
|
||||
var rows = this._spread_events();
|
||||
var row = jQuery('<div class="calendar_plannerEventRowWidget"></div>').appendTo(this.rows);
|
||||
var height = rows.length * (parseInt(window.getComputedStyle(row[0]).getPropertyValue("height")) || 20);
|
||||
var height = rows.length * this._row_height;
|
||||
var row_width = this.rows.width();
|
||||
row.remove();
|
||||
|
||||
for(var c = 0; c < rows.length; c++)
|
||||
{
|
||||
@ -725,7 +756,9 @@ var et2_calendar_planner_row = (function(){ "use strict"; return et2_valueWidget
|
||||
return;
|
||||
}
|
||||
|
||||
this.position_event();
|
||||
var row = jQuery('<div class="calendar_plannerEventRowWidget"></div>').appendTo(this.rows);
|
||||
this._row_height = (parseInt(window.getComputedStyle(row[0]).getPropertyValue("height")) || 20);
|
||||
row.remove();
|
||||
}
|
||||
|
||||
});}).call(this);
|
||||
|
@ -246,7 +246,13 @@ var et2_calendar_view = (function(){ "use strict"; return et2_valueWidget.extend
|
||||
_owner = jQuery.extend([],_owner);
|
||||
}
|
||||
this.options.owner = _owner;
|
||||
if(old !== this.options.owner && this.isAttached())
|
||||
if(this.isAttached() && (
|
||||
typeof old === "number" && typeof _owner === "number" && old !== this.options.owner ||
|
||||
// Array of ids will not compare as equal
|
||||
((typeof old === 'object' || typeof _owner === 'object') && old.toString() !== _owner.toString()) ||
|
||||
// Strings
|
||||
typeof old === 'string' && ''+old !== ''+this.options.owner
|
||||
))
|
||||
{
|
||||
this.invalidate(true);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user