diff --git a/calendar/js/app.js b/calendar/js/app.js index 4c16a8d8d8..cc3da6b849 100644 --- a/calendar/js/app.js +++ b/calendar/js/app.js @@ -1419,6 +1419,12 @@ app.classes.calendar = AppJS.extend( state = state ? JSON.parse(state) : {}; } + // Make sure date is consitantly a string, in case it needs to be passed to server + if(state.date.toJSON) + { + state.state = state.date.toJSON(); + } + // Don't store current user in state to allow admins to create favourites for all // Should make no difference for normal users. if(state.owner == egw.user('account_id')) @@ -1427,6 +1433,10 @@ app.classes.calendar = AppJS.extend( // it will work for other users too. state.owner = 0; } + // Don't store first and last + delete state.first; + delete state.last; + return state; }, @@ -1468,7 +1478,10 @@ app.classes.calendar = AppJS.extend( } } } - $j(this.sidebox_et2.getInstanceManager().DOMContainer).hide(); + if(this.sidebox_et2) + { + $j(this.sidebox_et2.getInstanceManager().DOMContainer).hide(); + } // Check for a supported client-side view if(this.views[state.state.view] && @@ -1675,6 +1688,9 @@ app.classes.calendar = AppJS.extend( } },this,et2_valueWidget); + // If current state matches a favorite, hightlight it + this.highlight_favorite(); + // Sidebox is updated, we can clear the flag this.state_update_in_progress = false; diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js index 8316698309..06cff79ff8 100644 --- a/etemplate/js/et2_extension_nextmatch.js +++ b/etemplate/js/et2_extension_nextmatch.js @@ -412,6 +412,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput, et2_IPrin */ applyFilters: function(_set) { var changed = false; + var keep_selection = false; // Avoid loops cause by change events if(this.update_in_progress) return; @@ -464,6 +465,17 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput, et2_IPrin } } } + else if (s === 'selected') + { + changed = true; + keep_selection = true; + this.controller._selectionMgr.resetSelection(); + for(var i in _set.selected) + { + this.controller._selectionMgr.setSelected(_set.selected[i].indexOf('::') > 0 ? _set.selected[i] : this.controller.dataStorePrefix + '::'+_set.selected[i],true); + } + delete _set.selected; + } else if (this.activeFilters[s] !== _set[s]) { this.activeFilters[s] = _set[s]; @@ -475,7 +487,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput, et2_IPrin this.egw().debug("info", "Changing nextmatch filters to ", this.activeFilters); // Keep the selection after applying filters, but only if unchanged - if(!changed) + if(!changed || keep_selection) { this.controller.keepSelection(); } @@ -509,6 +521,19 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput, et2_IPrin // Trigger an update this.controller.update(true); + if(changed) + { + // Highlight matching favorite in sidebox + if(this.getInstanceManager().app) + { + var app = this.getInstanceManager().app; + if(window.app[app] && window.app[app].highlight_favorite) + { + window.app[app].highlight_favorite(); + } + } + } + this.update_in_progress = false; }, diff --git a/phpgwapi/js/jsapi/app_base.js b/phpgwapi/js/jsapi/app_base.js index 0f6da51e5c..d8992b0251 100644 --- a/phpgwapi/js/jsapi/app_base.js +++ b/phpgwapi/js/jsapi/app_base.js @@ -142,6 +142,9 @@ var AppJS = Class.extend( this.et2 = et2.widgetContainer; this._fix_iFrameScrolling(); if (this.egw && this.egw.is_popup()) this._set_Window_title(); + + // Highlights the favorite based on initial list state + this.highlight_favorite(); }, /** @@ -370,10 +373,14 @@ var AppJS = Class.extend( li.siblings().removeClass('ui-state-highlight'); // Wait an arbitrary 50ms to avoid having the class removed again - // by the change handler. - window.setTimeout(function() { - li.addClass('ui-state-highlight'); - },50); + // by the change handler. + if(li.attr('data-id') !== 'blank') + { + window.setTimeout(function() { + li.addClass('ui-state-highlight'); + },50); + } + var href = jQuery('a[href^="javascript:"]', this).prop('href'); var matches = href ? href.match(/^javascript:([^\(]+)\((.*)?\);?$/) : null; if (matches && matches.length > 1 && matches[2] !== undefined) @@ -418,7 +425,7 @@ var AppJS = Class.extend( $j(egw_fw.applications[this.appname].browser.baseDiv) .off('.sidebox') .on('change.sidebox', function() { - $j('li',self.sidebox).removeClass('ui-state-highlight'); + self.highlight_favorite(); }); } return true; @@ -741,6 +748,101 @@ var AppJS = Class.extend( return false; }, + /** + * Mark the favorite closest matching the current state + * + * Closest matching takes into account not set values, so we pick the favorite + * with the most matching values without a value that differs. + */ + highlight_favorite: function() { + if(!this.sidebox) return; + + var state = this.getState(); + var best_match = false; + var best_count = 0; + + $j('li[data-id]',this.sidebox).removeClass('ui-state-highlight'); + + $j('li[data-id] a[href^="javascript:"]',this.sidebox).each(function(i,href) { + + var matches = href.href ? href.href.match(/^javascript:([^\(]+)\((.*)?\);?$/) : null; + var favorite = {} + if (matches && matches.length > 1 && matches[2] !== undefined) + { + favorite = JSON.parse(decodeURI(matches[2])); + } + if(!favorite || jQuery.isEmptyObject(favorite)) return; + + var match_count = 0; + for(var state_key in state) + { + if(state[state_key] == favorite.state[state_key] || !state[state_key] && !favorite.state[state_key]) + { + match_count++; + } + else if (state[state_key] && typeof state[state_key] === 'object' && favorite.state[state_key] && typeof favorite.state[state_key] === 'object') + { + if((typeof state[state_key].length !== 'undefined' || typeof state[state_key].length !== 'undefined') + && (state[state_key].length || Object.keys(state[state_key]).length) != (favorite.state[state_key].length || Object.keys(favorite.state[state_key]).length )) + { + // State or favorite has a length, but the other does not + if((state[state_key].length === 0 || Object.keys(state[state_key]).length === 0) && + (favorite.state[state_key].length == 0 || Object.keys(favorite.state[state_key]).length === 0)) + { + // Just missing, or one is an array and the other is an object + continue; + } + // One has a value and the other doesn't, no match + debugger; + return; + } + // Consider sub-objects (column filters) individually + for(var sub_key in state[state_key]) + { + if(state[state_key][sub_key] == favorite.state[state_key][sub_key] || !state[state_key][sub_key] && !favorite.state[state_key][sub_key]) + { + match_count++; + } + else if (typeof state[state_key][sub_key] === 'object' && typeof state[state_key][sub_key] === 'object') + { + // Too deep to keep going, just string compare for perfect match + if(state[state_key][sub_key].toJSON() === state[state_key][sub_key]) + { + match_count++; + } + } + else if(state[state_key][sub_key] && state[state_key][sub_key] != favorite.state[state_key][sub_key]) + { + // Different values, do not match + debugger; + return; + } + + } + } + else if (state_key == 'selectcols') + { + // Skip, might be set, might not + } + else if (typeof state[state_key] !== 'undefined' && state[state_key] != favorite.state[state_key]) + { + // Different values, do not match + debugger; + return; + } + } + if(match_count > best_count) + { + best_match = href.parentNode.dataset.id; + best_count = match_count; + } + }); + if(best_match) + { + $j('li[data-id="'+best_match+'"]',this.sidebox).addClass('ui-state-highlight'); + } + }, + /** * Fix scrolling iframe browsed by iPhone/iPod/iPad touch devices */