diff --git a/calendar/inc/class.calendar_ui.inc.php b/calendar/inc/class.calendar_ui.inc.php index 24150c15bd..cf2201ca82 100644 --- a/calendar/inc/class.calendar_ui.inc.php +++ b/calendar/inc/class.calendar_ui.inc.php @@ -796,6 +796,9 @@ class calendar_ui { $event['app_id'] .= ':'.egw_time::to($event['recur_date'] ? $event['recur_date'] : $event['start'],'ts'); } + // set id for grid + $event['row_id'] = $event['id'].($event['recur_type'] ? ':'.egw_time::to($event['recur_date'] ? $event['recur_date'] : $event['start'],'ts') : ''); + $event['parts'] = implode(",\n",$this->bo->participants($event,false)); $event['date'] = $this->bo->date2string($event['start']); diff --git a/calendar/inc/class.calendar_uilist.inc.php b/calendar/inc/class.calendar_uilist.inc.php index 4e5853ac83..8705ab206a 100644 --- a/calendar/inc/class.calendar_uilist.inc.php +++ b/calendar/inc/class.calendar_uilist.inc.php @@ -462,9 +462,6 @@ class calendar_uilist extends calendar_ui $event['edit_link'] = $this->popup($view_link).'; return false;'; } - // set id for grid - $event['row_id'] = $event['id'].($event['recur_type'] ? ':'.egw_time::to($event['recur_date'] ? $event['recur_date'] : $event['start'],'ts') : ''); - // Format start and end with timezone foreach(array('start','end') as $time) { diff --git a/calendar/inc/class.calendar_uiviews.inc.php b/calendar/inc/class.calendar_uiviews.inc.php index b33f6ec93c..ce5f3bce9d 100644 --- a/calendar/inc/class.calendar_uiviews.inc.php +++ b/calendar/inc/class.calendar_uiviews.inc.php @@ -661,20 +661,36 @@ class calendar_uiviews extends calendar_ui $content = array('view' => array()); - // Always do 7 days for a week so scrolling works properly - $this->last = ($days == 4 ? $this->last : $search_params['end'] = strtotime("+$days days",$this->first) - 1); - if (count($users) == 1 || count($users) >= $this->cal_prefs['week_consolidate']) // for more then X users, show all in one row + if(!$home) { - $content['view'][] = (array)$this->tagWholeDayOnTop($this->bo->search($search_params)) + - array('owner' => $users); + // Fill with the minimum needed 'weeks' + $min = max( + 6, // Some months need 6 weeks for full display + $this->cal_prefs['multiple_weeks'], // WeekN view + $this->cal_prefs['week_consolidate'] // We collapse after this many users + ); + for($i = 0; $i < $min; $i++) + { + $content['view'][] = array(); + } } else { - foreach($this->_get_planner_users(false) as $uid => $label) + // Always do 7 days for a week so scrolling works properly + $this->last = ($days == 4 ? $this->last : $search_params['end'] = strtotime("+$days days",$this->first) - 1); + if (count($users) == 1 || count($users) >= $this->cal_prefs['week_consolidate']) // for more then X users, show all in one row { - $search_params['users'] = $uid; - $content['view'][] = $this->tagWholeDayOnTop($this->bo->search($search_params)) - + array('owner' => $uid); + $content['view'][] = (array)$this->tagWholeDayOnTop($this->bo->search($search_params)) + + array('owner' => $users); + } + else + { + foreach($this->_get_planner_users(false) as $uid => $label) + { + $search_params['users'] = $uid; + $content['view'][] = $this->tagWholeDayOnTop($this->bo->search($search_params)) + + array('owner' => $uid); + } } } $tmpl = $home ? $home :new etemplate_new('calendar.view'); diff --git a/calendar/js/app.js b/calendar/js/app.js index 088b5f325e..1bbe2b56c2 100644 --- a/calendar/js/app.js +++ b/calendar/js/app.js @@ -1594,7 +1594,7 @@ app.classes.calendar = AppJS.extend( * * @param {Object} _set New settings */ - update_state: function(_set) + update_state: function update_state(_set) { // Make sure we're running in top window if(window !== window.top) @@ -1632,7 +1632,7 @@ app.classes.calendar = AppJS.extend( * * @return {object} description */ - getState: function() + getState: function getState() { var state = jQuery.extend({},this.state); @@ -1683,7 +1683,7 @@ app.classes.calendar = AppJS.extend( * * @param {object} state containing "name" attribute to be used as "favorite" GET parameter to a nextmatch */ - setState: function(state) + setState: function setState(state) { // State should be an object, not a string, but we'll parse if(typeof state == "string") @@ -1730,7 +1730,7 @@ app.classes.calendar = AppJS.extend( } // Check for valid cache - var cachable_changes = ['date','view','days','planner_days','sortby']; + var cachable_changes = ['date','weekend','view','days','planner_days','sortby']; var keys = jQuery.unique(Object.keys(this.state).concat(Object.keys(state.state))); for(var i = 0; i < keys.length; i++) { @@ -1838,19 +1838,18 @@ app.classes.calendar = AppJS.extend( var grid = view.etemplates[0].widgetContainer.getWidgetById('view'); /* - If the count is different, we need to have the correct number (just remove all & re-create) + If the count is different, we need to have the correct number If the count is > 1, it's either because there are multiple date spans (weekN, month) and we need the correct span per row, or there are multiple owners and we need the correct owner per row. */ - if(grid && (grid_count !== grid._children.length || grid_count > 1)) + if(grid) { - // Need to redo the number of grids var value = []; state.state.first = view.start_date(state.state).toJSON(); // We'll modify this one, so it needs to be a new object var date = new Date(state.state.first); - // Determine the different end date + // Determine the different end date & varying values switch(state.state.view) { case 'month': @@ -1861,59 +1860,147 @@ app.classes.calendar = AppJS.extend( for(var week = 0; week < grid_count; week++) { var val = { - id: ""+date.getUTCFullYear() + sprintf("%02d",date.getUTCMonth()) + sprintf("%02d",date.getUTCDate()), + id: app.classes.calendar._daywise_cache_id(date,state.state.owner), start_date: date.toJSON(), end_date: new Date(date.toJSON()), owner: state.state.owner }; val.end_date.setUTCHours(24*7-1); + val.end_date.setUTCMinutes(59); + val.end_date.setUTCSeconds(59); val.end_date = val.end_date.toJSON(); value.push(val); date.setUTCHours(24*7); } state.state.last=val.end_date; break; + case 'day': + var end = state.state.last = view.end_date(state.state).toJSON(); + value.push({ + id: app.classes.calendar._daywise_cache_id(date,state.state.owner), + start_date: state.state.first, + end_date: state.state.last, + owner: view.owner(state.state) + }); + break; default: var end = state.state.last = view.end_date(state.state).toJSON(); for(var owner = 0; owner < grid_count && owner < state.state.owner.length; owner++) { + var _owner = grid_count > 1 ? state.state.owner[owner] || 0 : state.state.owner value.push({ - id: ""+date.getUTCFullYear() + sprintf("%02d",date.getUTCMonth()) + sprintf("%02d",date.getUTCDate()), + id: app.classes.calendar._daywise_cache_id(date,_owner), start_date: date, end_date: end, - owner: grid_count > 1 ? state.state.owner[owner] || 0 : state.state.owner + owner: _owner }); } break; } // If we have cached data for the timespan, pass it along this._need_data(value,state.state); - if(grid) + + var row_index = 0; + + // Find any matching, existing rows - they can be kept + grid.iterateOver(function(widget) { + for(var i = 0; i < value.length; i++) + { + if(widget.id == value[i].id) + { + // Keep it, but move it + if(i > row_index) + { + for(var j = i-row_index; j > 0; j--) + { + // Move from the end to the start + grid._children.unshift(grid._children.pop()); + + // Swap DOM nodes + var a = grid._children[0].getDOMNode().parentNode.parentNode; + var b = grid._children[1].getDOMNode().parentNode.parentNode; + a.parentNode.insertBefore(a,b); + } + } + else if (row_index > i) + { + for(var j = row_index - i; j > 0; j--) + { + // Move from the start to the end + grid._children.push(grid._children.shift()); + + // Swap DOM nodes + var a = grid._children[grid._children.length - 1].getDOMNode().parentNode.parentNode; + a.parentNode.insertBefore(a,null); + } + } + break; + } + } + row_index++; + },this,et2_calendar_view); + row_index = 0; + + // Set rows that need it + grid.iterateOver(function(widget) { + if(row_index < value.length) + { + widget.set_disabled(false); + } + else + { + widget.set_disabled(true); + return; + } + if(widget.set_show_weekend) + { + widget.set_show_weekend(view.show_weekend(state.state)); + } + if(widget.id == value[row_index].id && + widget.get_end_date().toJSON() == value[row_index].end_date + ) + { + // Do not need to re-set this row, but we do need to re-do + // the times, as they may have changed + widget.invalidate(); + row_index++; + return; + } + if(widget.set_value) + { + widget.set_value(value[row_index++]); + } + },this, et2_calendar_view); + grid.iterateOver(function(widget) { + if(widget.set_granularity) + { + widget.set_granularity(view.granularity(state.state)); + } + if(widget.resize) + { + widget.resize(); + } + },this,et2_calendar_view); + + // Single day with multiple owners still needs owners split to satisfy + // caching keys, otherwise they'll cache consolidated + if(state.state.view == 'day' && state.state.owner.length < parseInt(this.egw.preference('day_consolidate','calendar'))) { - grid.set_value( - {content: value} - ); - - // Weekend needs to be done seperately - grid.iterateOver(function(widget) { - if(widget.set_show_weekend) - { - widget.set_show_weekend(view.show_weekend(state.state)); - } - },this, et2_calendar_view); - - // Granularity needs to be done seperately - grid.iterateOver(function(widget) { - if(widget.set_granularity) - { - widget.set_granularity(view.granularity(state.state)); - } - },this, et2_calendar_view); + value = []; + for(var i = 0; i < state.state.owner.length; i++) + { + value.push({ + start_date: state.state.first, + end_date: state.state.last, + owner: state.state.owner[i] + }); + } + this._need_data(value,state.state); } } else { - // Simple, easy case - just one widget for the selected time span. + // Simple, easy case - just one widget for the selected time span. (planner) // Update existing view's special attribute filters, defined in the view list for(var updater in view) { @@ -1929,26 +2016,13 @@ app.classes.calendar = AppJS.extend( view.etemplates[i].widgetContainer.iterateOver(function(widget) { if(typeof widget['set_'+updater] === 'function') { - widget['set_'+updater](value); + widget['set_'+updater](value); } }, this, et2_calendar_view); } } } var value = [{start_date: state.state.first, end_date: state.state.last}]; - // Single day with multiple owners still needs owners split - if(state.state.view == 'day' && state.state.owner.length < parseInt(this.egw.preference('day_consolidate','calendar'))) - { - value = []; - for(var i = 0; i < state.state.owner.length; i++) - { - value.push({ - start_date: state.state.first, - end_date: state.state.last, - owner: state.state.owner[i] - }); - } - } this._need_data(value,state.state); } // Include first & last dates in state, mostly for server side processing @@ -2488,7 +2562,7 @@ app.classes.calendar = AppJS.extend( {start: start, num_rows:200}, query, this.id, - function(data) { + function calendar_handleResponse(data) { console.log(data); // Look for any updated select options if(data.rows && data.rows.sel_options && this.sidebox_et2) @@ -3282,10 +3356,6 @@ jQuery.extend(app.classes.calendar,{ // Always 7 days, we just turn weekends on or off d.setUTCHours(24*7-1); return d; - }, - granularity: function(state) { - // Does not care how many users you select - return parseInt(egw.preference('interval','calendar')) || 30; } }), month: app.classes.calendar.prototype.View.extend({ diff --git a/calendar/js/et2_widget_daycol.js b/calendar/js/et2_widget_daycol.js index 791848e52f..3c151e7720 100644 --- a/calendar/js/et2_widget_daycol.js +++ b/calendar/js/et2_widget_daycol.js @@ -93,6 +93,8 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM], // Percentage; not yet available titleHeight: 2.0 } + + this.registeredUID = null; }, doLoadingFinished: function() { @@ -122,7 +124,7 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM], this.date_helper.destroy(); this.date_helper = null; - egw.dataUnregisterUID(app.classes.calendar._daywise_cache_id(this.options.date,this.options.owner),false,this); + egw.dataUnregisterUID(this.registeredUID,false,this); }, /** @@ -248,9 +250,11 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM], return; } - if(this.options.date) + var cache_id = app.classes.calendar._daywise_cache_id(new_date,this.options.owner); + if(this.options.date && this.registeredUID && + cache_id !== this.registeredUID) { - egw.dataUnregisterUID(app.classes.calendar._daywise_cache_id(this.options.date,this.options.owner),false,this); + egw.dataUnregisterUID(this.registeredUID,false,this); // Remove existing events while(this._children.length > 0) @@ -271,11 +275,10 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM], // Register for updates on events for this day - var cache_id = app.classes.calendar._daywise_cache_id(new_date,this.options.owner); - egw.dataRegisterUID(cache_id, this._data_callback,this,this.getInstanceManager().execId,this.id); - - if(events) { - this._update_events(events); + if(this.registeredUID !== cache_id) + { + this.registeredUID = cache_id; + egw.dataRegisterUID(this.registeredUID, this._data_callback,this,this.getInstanceManager().execId,this.id); } }, @@ -295,17 +298,22 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM], return; } - egw.dataUnregisterUID(app.classes.calendar._daywise_cache_id(this.options.date,this.options.owner),false,this); + var cache_id = app.classes.calendar._daywise_cache_id(this.options.date,_owner) + if(this.options.date && this.registeredUID && + cache_id !== this.registeredUID) + { + egw.dataUnregisterUID(this.registeredUID,false,this); + } this.options.owner = _owner; this.title .attr("data-owner", this.options.owner); - // Register for updates on events for this day - egw.dataRegisterUID( - app.classes.calendar._daywise_cache_id(this.options.date,this.options.owner), - this._data_callback,this,this.getInstanceManager().execId,this.id - ); + if(this.registeredUID !== cache_id) + { + this.registeredUID = cache_id; + egw.dataRegisterUID(this.registeredUID, this._data_callback,this,this.getInstanceManager().execId,this.id); + } }, /** @@ -445,22 +453,20 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM], { // Create event var event = et2_createWidget('calendar-event',{ - id:events[c].id, + id:'event_'+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.length; c++) { - this.getWidgetById(events[c].id).set_value(events[c]); + var event = this.getWidgetById('event_'+events[c].id); + if(!event) continue; + if(this.isInTree()) + { + event.doLoadingFinished(); + } } // Apply styles to hidden events diff --git a/calendar/js/et2_widget_event.js b/calendar/js/et2_widget_event.js index eab6952afe..be76f7eea9 100644 --- a/calendar/js/et2_widget_event.js +++ b/calendar/js/et2_widget_event.js @@ -97,6 +97,16 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM], this.options.date = ''; this.set_date(date); } + if(this.options.value && this.options.value.row_id) + { + egw.dataRegisterUID( + 'calendar::'+this.options.value.row_id, + this._UID_callback , + this, + this.getInstanceManager().execId, + this.id + ); + } }, destroy: function() { @@ -131,47 +141,60 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM], // Un-register for updates if(this.options.value) { - var old_app_id = this.options.value.app_id; - egw.dataUnregisterUID('calendar::'+old_app_id,false,this); + var old_id = this.options.value.row_id; + if(!_value || !_value.row_id || old_id !== _value.row_id) + { + egw.dataUnregisterUID('calendar::'+old_id,false,this); + } } this.options.value = _value; // Register for updates - var app_id = this.options.value.app_id; - egw.dataRegisterUID('calendar::'+app_id, function _UID_callback(event) { - // Make sure id is a string - this._values_check(event); - - // Check for changing days in the grid view - if(!this._sameday_check(event)) - { - // This should now cease to exist, as new events have been created - this.free(); - return; - } - - // Copy to avoid changes, which may cause nm problems - this.options.value = jQuery.extend({},event); - - if(this._parent.options.date) - { - this.options.value.date = this._parent.options.date; - } - - // Let parent position - this._parent.position_event(this); - - // Parent may remove this if the date isn't the same - if(this._parent) - { - this._update(this.options.value); - } - - },this,this.getInstanceManager().execId,this.id); - - if(_value && !egw.dataHasUID('calendar::'+app_id)) + var id = this.options.value.row_id; + if(!old_id || old_id !== id) { - egw.dataStoreUID('calendar::'+app_id, _value); + egw.dataRegisterUID('calendar::'+id, this._UID_callback ,this,this.getInstanceManager().execId,this.id); + } + if(_value && !egw.dataHasUID('calendar::'+id)) + { + egw.dataStoreUID('calendar::'+id, _value); + } + }, + + _UID_callback: function _UID_callback(event) { + // Make sure id is a string + this._values_check(event); + + // Check for changing days in the grid view + if(!this._sameday_check(event)) + { + // This should now cease to exist, as new events have been created + this.free(); + return; + } + + // Copy to avoid changes, which may cause nm problems + this.options.value = jQuery.extend({},event); + + if(this._parent.options.date) + { + this.options.value.date = this._parent.options.date; + } + + // Let parent position + this._parent.position_event(this); + + // Parent may remove this if the date isn't the same + if(this._parent) + { + // This gives some slight speed enhancements over doing it immediately, + // but it looks weird + /* + window.setTimeout(jQuery.proxy(function() { + if(this.options) this._update(this.options.value); + },this),100); + */ + this._update(this.options.value); } }, @@ -180,15 +203,19 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM], // Copy new information this.options.value = event; - var app_id = event.app_id ? event.app_id : event.id + (event.recur_type ? ':'+event.recur_date : ''); + var id = event.row_id ? event.row_id : event.id + (event.recur_type ? ':'+event.recur_date : ''); this._parent.date_helper.set_value(event.start.valueOf ? new Date(event.start) : event.start); var formatted_start = this._parent.date_helper.getValue(); - this.set_id('event_' + event.app_id); + this.set_id('event_' + id); if(this._actionObject) { - this._actionObject.id = 'calendar::' + event.app_id; + this._actionObject.id = 'calendar::' + id; } + + // Copy actions set in parent + this._link_actions(this._parent._parent._parent.options.actions||{}); + // Make sure category stuff is there // Fake it to use the cache / call - if already there, these will return // immediately. @@ -217,7 +244,7 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM], // Put everything we need for basic interaction here, so it's available immediately .attr('data-id', event.id) .attr('data-app', event.app || 'calendar') - .attr('data-app_id', app_id) + .attr('data-app_id', event.app_id) .attr('data-start', formatted_start) .attr('data-owner', event.owner) .attr('data-recur_type', event.recur_type) diff --git a/calendar/js/et2_widget_planner.js b/calendar/js/et2_widget_planner.js index b0687efb36..8dd0f45af2 100644 --- a/calendar/js/et2_widget_planner.js +++ b/calendar/js/et2_widget_planner.js @@ -692,30 +692,29 @@ var et2_calendar_planner = et2_calendar_view.extend([et2_IDetachedDOM, et2_IResi window.clearTimeout(this.update_timer); } this.update_timer = window.setTimeout(jQuery.proxy(function() { - this.doInvalidate = false; - - this.widget.value = this.widget._fetch_data(); + this.widget.doInvalidate = false; // Show AJAX loader - framework.applications.calendar.sidemenuEntry.showAjaxLoader(); + this.widget.loader.show(); + + this.widget.value = this.widget._fetch_data(); this.widget._drawGrid(); // Update actions - if(this._actionManager) + if(this.widget._actionManager) { - this._link_actions(this._actionManager.children); + this.widget._link_actions(this.widget._actionManager.children); } - // Hide AJAX loader - framework.applications.calendar.sidemenuEntry.hideAjaxLoader(); - if(this.trigger) { this.widget.change(); } this.widget.update_timer = null; - this.doInvalidate = true; + this.widget.doInvalidate = true; + + window.setTimeout(jQuery.proxy(function() {this.loader.hide();},this.widget),100); },{widget:this,"trigger":trigger}),ET2_GRID_INVALIDATE_TIMEOUT); }, diff --git a/calendar/js/et2_widget_planner_row.js b/calendar/js/et2_widget_planner_row.js index 97c5963d70..ef42444820 100644 --- a/calendar/js/et2_widget_planner_row.js +++ b/calendar/js/et2_widget_planner_row.js @@ -227,22 +227,20 @@ var et2_calendar_planner_row = et2_valueWidget.extend([et2_IDetachedDOM], { // Create event var event = et2_createWidget('calendar-event',{ - id:events[c].id, + id:'event_'+events[c].row_id, value: events[c] },this); + } + + // Seperate loop so column sorting finds all children in the right place + for(var c = 0; c < events.length && c < this._children.length; c++) + { + var event = this.getWidgetById('event_'+events[c].row_id); + if(!event) continue; if(this.isInTree()) { event.doLoadingFinished(); } - - // Copy actions set in parent - event._link_actions(this._parent._parent.options.actions||this._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.length; c++) - { - this.getWidgetById(events[c].id).set_value(events[c]); } }, diff --git a/calendar/js/et2_widget_timegrid.js b/calendar/js/et2_widget_timegrid.js index 5121581231..2af6aafb35 100644 --- a/calendar/js/et2_widget_timegrid.js +++ b/calendar/js/et2_widget_timegrid.js @@ -96,7 +96,8 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes // Contains times / rows this.scrolling = $j(document.createElement('div')) .addClass("calendar_calTimeGridScroll") - .appendTo(this.div); + .appendTo(this.div) + .append('
'); // Contains days / columns this.days = $j(document.createElement("div")) @@ -500,25 +501,31 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes this.day_list = []; // Wait a bit to see if anything else changes, then re-draw the days - if(this.update_timer === null) + if(this.update_timer) { - this.update_timer = window.setTimeout(jQuery.proxy(function() { - this.widget.update_timer = null; - - // Update actions - if(this.widget._actionManager) - { - this.widget._link_actions(this.widget._actionManager.children); - } - - this.widget._drawDays(); - this.widget._resizeTimes(); - if(this.trigger) - { - this.widget.change(); - } - },{widget:this,"trigger":trigger}),ET2_GRID_INVALIDATE_TIMEOUT); + window.clearTimeout(this.update_timer); } + this.update_timer = window.setTimeout(jQuery.proxy(function() { + this.widget.update_timer = null; + this.widget.loader.hide().show(); + + // Update actions + if(this.widget._actionManager) + { + this.widget._link_actions(this.widget._actionManager.children); + } + + this.widget._drawDays(); + // We have to completely re-do times, as they may have changed in + // scale to the point where more labels are needed / need to be removed + this.widget._drawTimes(); + if(this.trigger) + { + this.widget.change(); + } + // Hide loader + window.setTimeout(jQuery.proxy(function() {this.loader.hide();},this.widget),100); + },{widget:this,"trigger":trigger}),ET2_GRID_INVALIDATE_TIMEOUT); }, detachFromDOM: function() { @@ -562,6 +569,14 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes } }, + set_disabled: function(disabled) { + this._super.apply(this, arguments); + if(disabled) + { + this.loader.show(); + } + }, + /** * Clear everything, and redraw the whole grid */ @@ -569,6 +584,7 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes this.div.css('height', this.options.height) .empty(); + this.loader.prependTo(this.div).show(); // Draw in the horizontal - the times this._drawTimes(); @@ -605,7 +621,6 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes this.scrolling .css('height', (this.div.innerHeight() - header_height)+'px') .appendTo(this.div) - .empty() .off().on('scroll', jQuery.proxy(this._scroll, this)); // Percent @@ -616,8 +631,16 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes // We need a reasonable bottom limit here... if(this.rowHeight < 5 && this.div.is(':visible')) { - this.options.granularity *= 2; - return this._drawTimes(); + if(this.rowHeight === 0) + { + // Something is not right... + this.rowHeight = 5; + } + else + { + this.options.granularity *= 2; + return this._drawTimes(); + } } // the hour rows @@ -659,8 +682,9 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes } // Set heights in pixels for scrolling - this.scrolling - .append('