/**
* EGroupware eTemplate2 - JS Favorite widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2013
* @version $Id$
*/
"use strict";
/*egw:uses
et2_dropdown_button;
et2_extension_nextmatch;
*/
/**
* Favorites widget, designed for use with a nextmatch widget
*
* The primary control is a split/dropdown button. Clicking on the left side of the button filters the
* nextmatch list by the user's default filter. The right side of the button gives a list of
* saved filters, pulled from preferences. Clicking a filter from the dropdown list sets the
* filters as saved.
*
* Favorites can also automatically be shown in the sidebox, using the special ID favorite_sidebox.
* Use the following code to generate the sidebox section:
* display_sidebox($appname,lang('Favorites'),array(
* array(
* 'no_lang' => true,
* 'text'=>'',
* 'link'=>false,
* 'icon' => false
* )
* ));
* This sidebox list will be automatically generated and kept up to date.
*
*
* Favorites are implemented by saving the values for [column] filters. Filters are stored
* in preferences, with the name favorite_. The favorite favorite used for clicking on
* the filter button is stored in nextmatch--favorite.
*
* @augments et2_dropdown_button
*/
var et2_favorites = et2_dropdown_button.extend([et2_INextmatchHeader],
{
attributes: {
"default_pref": {
"name": "Default preference key",
"type": "string",
"description": "The preference key where default favorite is stored (not the value)"
},
"sidebox_target": {
"name": "Sidebox target",
"type": "string",
"description": "ID of element to insert favorite list into",
"default": "favorite_sidebox"
},
"app": {
"name": "Application",
"type": "string",
"description": "Application to show favorites for"
},
"filters": {
"name": "Extra filters",
"type": "any",
"description": "Array of extra filters to include in the saved favorite"
},
// These are particular to favorites
id: {"default": "favorite"},
label: {"default": ""},
label_updates: { "default": false},
image: {"default": "etemplate/fav_filter"},
statustext: {"default": "Favorite queries", "type": "string"}
},
// Some convenient variables, used in closures / event handlers
header: null,
nextmatch: null,
favorite_prefix: "favorite_",
stored_filters: {},
// If filter was set server side, we need to remember it until nm is created
nm_filter: false,
/**
* Constructor
*
* @memberOf et2_favorites
*/
init: function() {
this._super.apply(this, arguments);
this.sidebox_target = $j("#"+this.options.sidebox_target);
if(this.sidebox_target.length == 0 && egw_getFramework() != null)
{
var egw_fw = egw_getFramework();
this.sidebox_target = $j("#"+this.options.sidebox_target,egw_fw.sidemenuDiv);
}
var apps = egw().user('apps');
this.is_admin = (typeof apps['admin'] != "undefined");
this.stored_filters = this.load_favorites(this.options.app);
this.preferred = egw.preference(this.options.default_pref,this.options.app);
if(!this.preferred || typeof this.stored_filters[this.preferred] == "undefined")
{
this.preferred = "blank";
}
// It helps to have the ID properly set before we get too far
this.set_id(this.id);
this.init_filters(this);
this.menu.addClass("favorites");
// Set the default (button) value
this.set_value(this.preferred,true);
// If a pre-selected filter was passed from server
if(typeof this.options.value != "undefined")
{
this.set_value(this.options.value);
}
var self = this;
// Initialize sidebox
this._init_sidebox();
// Add a listener on the radio buttons to set default filter
$j(this.menu).on("click","input:radio", function(event){
// Don't do the menu
event.stopImmediatePropagation();
// Save as default favorite - used when you click the button
self.egw().set_preference(self.options.app,self.options.default_pref,$j(this).val());
self.preferred = $j(this).val();
// Update sidebox, if there
if(self.sidebox_target.length)
{
self.sidebox_target.find(".ui-icon-heart")
.replaceWith("");
$j("li[data-id='"+self.preferred+"'] img",self.sidebox_target)
.replaceWith("");
}
// Close the menu
self.menu.hide();
// Some user feedback
self.button.addClass("ui-state-active", 500,"swing",function(){
self.button.removeClass("ui-state-active",2000);
});
});
// Add a listener on the delete to remove
this.menu.on("click","div.ui-icon-trash", this, this.delete_favorite)
// Wrap and unwrap because jQueryUI styles use a parent, and we don't want to change the state of the menu item
// Wrap in a span instead of a div because div gets a border
.on("mouseenter","div.ui-icon-trash", function() {$j(this).wrap("");})
.on("mouseleave","div.ui-icon-trash", function() {$j(this).unwrap();});
// Trigger refresh of menu options now that events are registered
// to update sidebox
if(this.sidebox_target.length > 0)
{
this.init_filters(this);
}
},
destroy: function() {
if(this.popup != null)
{
if(this.popup.group)
{
this.popup.group.free();
delete this.popup.group;
}
this.popup.dialog("destroy");
this.popup = null;
}
if(this.sidebox_target.length)
{
this.sidebox_target
.off()
.empty();
}
this._super.apply(this, arguments);
},
/**
* Load favorites from preferences
*
* @param app String Load favorites from this application
*/
load_favorites: function(app) {
// Default blank filter
var stored_filters = {
'blank': {
name: this.egw().lang("No filters"),
filters: {},
}
};
// Load saved favorites
var preferences = egw.preference("*",app);
for(var pref_name in preferences)
{
if(pref_name.indexOf(this.favorite_prefix) == 0)
{
var name = pref_name.substr(this.favorite_prefix.length);
stored_filters[name] = preferences[pref_name];
}
}
if(typeof stored_filters == "undefined" || !stored_filters)
{
stored_filters = {};
}
return stored_filters;
},
/**
* Delete a favorite from the list and update preferences
* Registered as a handler on the delete icons
*/
delete_favorite: function(event)
{
// Don't do the menu
event.stopImmediatePropagation();
var header = event.data;
var name = $j(this).parentsUntil("li").parent().attr("data-id");
var trash = this;
// Make sure first
var do_delete = function(button_id)
{
if(button_id != et2_dialog.YES_BUTTON) return;
// Hide the trash
$j(trash).hide();
// Delete preference server side
var request = egw.json("etemplate_widget_nextmatch::ajax_set_favorite::etemplate",
[header.app, name, "delete", header.stored_filters[name].group ? header.stored_filters[name].group : '', ''],
function(result) {
if(result)
{
// Remove line from list
this.slideUp("slow", function() { header.menu.hide();});
delete header.stored_filters[name];
header.init_filters(header);
}
},
$j(trash).parentsUntil("li").parent(),
true,
$j(trash).parentsUntil("li").parent()
);
request.sendRequest();
}
et2_dialog.show_dialog(do_delete, (header.egw().lang("Delete") + " " +header.stored_filters[name].name +"?"),
"Delete", et2_dialog.YES_NO, et2_dialog.QUESTION_MESSAGE);
},
// Create & set filter options for dropdown menu
init_filters: function(widget, filters)
{
if(typeof filters == "undefined")
{
filters = this.stored_filters;
}
var options = {};
for(var name in filters)
{
options[name] = ""+
(filters[name].name != undefined ? filters[name].name : name) +
(filters[name].group != false ? " ♦" :"") +
(filters[name].group != false && !this.is_admin || name == 'blank' ? "" :
"");
}
// Only add 'Add current' if we have a nextmatch
if(this.nextmatch)
{
options.add = "Add current";
}
widget.set_select_options.call(widget,options);
// Set radio to current value
$j("input[value='"+ this.preferred +"']:radio", this.menu).attr("checked",true);
// Clone for sidebox
if(this.sidebox_target.length)
{
this.sidebox_target.empty();
var sidebox_clone = widget.menu.clone();
sidebox_clone
.appendTo(this.sidebox_target)
.removeAttr('style')
.menu()
.removeClass("ui-widget")
.show()
.find("input:checked").replaceWith("");
sidebox_clone
.find("input").replaceWith("");
}
},
/**
* Find sidebox, if not found yet, and register handlers
*/
_init_sidebox: function()
{
// Sometimes the sidebox is not loaded when the template is created (jdots)
if(this.options && (!this.sidebox_target || this.sidebox_target.length == 0))
{
this.sidebox_target = $j("#"+this.options.sidebox_target);
if(this.sidebox_target.length == 0 && egw_getFramework() != null)
{
var egw_fw = egw_getFramework();
this.sidebox_target = $j("#"+this.options.sidebox_target,egw_fw.sidemenuDiv);
}
if(this.sidebox_target.length == 0)
{
// Still no sidebox - might be loaded via ajax later, so we'll do this on first mouse over
$j('body').on('mouseover','#'+this.options.sidebox_target,
jQuery.proxy(function(e) {
// Set up handlers & such
this._init_sidebox();
// It will still have the plain HTML, so re-create the contents
this.init_filters(this);
$j('body').off(e);
},this)
);
}
}
if(this.sidebox_target.length)
{
var self = this;
this.sidebox_target
.off()
.on("mouseenter","div.ui-icon-trash", function() {$j(this).wrap("");})
.on("mouseleave","div.ui-icon-trash", function() {$j(this).unwrap();})
.on("click","div.ui-icon-trash", this, this.delete_favorite)
.addClass("ui-helper-clearfix");
this.sidebox_target.on("click","li",function() {
self.set_value($j(this).attr("data-id"));
self.change(this);
});
}
},
set_nm_filters: function(filters)
{
if(this.nextmatch)
{
this.nextmatch.activeFilters = filters;
this.nextmatch.applyFilters();
}
else
{
console.log(filters);
}
},
onclick: function(node) {
// Apply preferred filter - make sure it's an object, and not a reference
if(this.preferred && this.stored_filters[this.preferred])
{
this.set_nm_filters(jQuery.extend({},this.stored_filters[this.preferred].filter));
}
else
{
alert(this.egw().lang("No default set"));
}
},
// Apply the favorite when you pick from the list
change: function(selected_node) {
this.value = $j(selected_node).attr("data-id");
if(this.value == "add" && this.nextmatch)
{
// Get current filters
this.popup.current_filters = $j.extend({},this.nextmatch.activeFilters);
// Add in extras
for(var extra in this.options.filters)
{
// Don't overwrite what nm has, chances are nm has more up-to-date value
if(typeof this.popup.current_filters == 'undefined')
{
this.popup.current_filters[extra] = this.nextmatch.options.settings[extra];
}
}
// Skip columns for now
delete this.popup.current_filters.selcolumns;
// Add in application's settings
if(this.filters != true)
{
for(var i = 0; i < this.filters.length; i++)
{
this.popup.current_filters[this.options.filters[i]] = this.nextmatch.options.settings[this.options.filters[i]];
}
}
// Remove some internal values
delete this.popup.current_filters[this.id];
if(this.popup.group != undefined)
{
delete this.popup.current_filters[this.popup.group.id];
}
// Make sure it's an object - deep copy to prevent references in sub-objects (col_filters)
this.popup.current_filters = jQuery.extend(true,{},this.popup.current_filters);
// Update popup with current set filters (more for debug than user)
var filter_list = [];
var add_to_popup = function(arr) {
filter_list.push("