2013-05-22 22:13:12 +02:00
|
|
|
/**
|
2013-05-29 21:25:12 +02:00
|
|
|
* EGroupware - Home - Javascript UI
|
2013-05-22 22:13:12 +02:00
|
|
|
*
|
|
|
|
* @link http://www.egroupware.org
|
2013-05-29 21:25:12 +02:00
|
|
|
* @package home
|
|
|
|
* @author Nathan Gray
|
|
|
|
* @copyright (c) 2013 Nathan Gray
|
2013-05-22 22:13:12 +02:00
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/*egw:uses
|
|
|
|
jquery.jquery;
|
|
|
|
jquery.jquery-ui;
|
|
|
|
/phpgwapi/js/jquery/gridster/jquery.gridster.js;
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* JS for home application
|
|
|
|
*
|
|
|
|
* Home is a collection of little bits of content (portlets) from the other applications.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Uses Gridster for the grid layout
|
2013-05-29 21:25:12 +02:00
|
|
|
* @see http://gridster.net
|
2013-05-22 22:13:12 +02:00
|
|
|
* @augments AppJS
|
|
|
|
*/
|
|
|
|
app.home = AppJS.extend(
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* AppJS requires overwriting this with the actual application name
|
|
|
|
*/
|
|
|
|
appname: "home",
|
|
|
|
|
2013-05-29 21:25:12 +02:00
|
|
|
/**
|
|
|
|
* Grid resolution. Must match et2_portlet GRID
|
|
|
|
*/
|
|
|
|
GRID: 50,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default size for new portlets
|
|
|
|
*/
|
|
|
|
DEFAULT: {
|
|
|
|
WIDTH: 2,
|
|
|
|
HEIGHT: 1
|
|
|
|
},
|
|
|
|
|
2013-05-22 22:13:12 +02:00
|
|
|
/**
|
|
|
|
* Constructor
|
2013-05-23 00:44:27 +02:00
|
|
|
*
|
|
|
|
* @memberOf app.home
|
|
|
|
*/
|
|
|
|
init: function()
|
|
|
|
{
|
|
|
|
// call parent
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor
|
|
|
|
* @memberOf app.home
|
|
|
|
*/
|
|
|
|
destroy: function()
|
|
|
|
{
|
|
|
|
delete this.et2;
|
2013-05-22 22:13:12 +02:00
|
|
|
delete this.portlet_container;
|
|
|
|
|
|
|
|
// call parent
|
2013-05-23 00:44:27 +02:00
|
|
|
this._super.apply(this, arguments);
|
|
|
|
},
|
2013-05-22 22:13:12 +02:00
|
|
|
|
2013-05-23 00:44:27 +02:00
|
|
|
/**
|
|
|
|
* This function is called when the etemplate2 object is loaded
|
|
|
|
* and ready. If you must store a reference to the et2 object,
|
|
|
|
* make sure to clean it up in destroy().
|
|
|
|
*
|
|
|
|
* @param et2 etemplate2 Newly ready object
|
|
|
|
*/
|
|
|
|
et2_ready: function(et2)
|
|
|
|
{
|
|
|
|
// call parent
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
2013-05-22 22:13:12 +02:00
|
|
|
this.portlet_container = this.et2.getWidgetById("portlets");
|
2013-09-17 19:04:57 +02:00
|
|
|
|
|
|
|
// Don't do twice
|
|
|
|
if(this.portlet_container._children.length > 0) return;
|
2013-05-22 22:13:12 +02:00
|
|
|
|
|
|
|
// Add portlets
|
|
|
|
var content = this.et2.getArrayMgr("content").getEntry("portlets");
|
|
|
|
var modifications = this.et2.getArrayMgr("modifications").getEntry("portlets");
|
|
|
|
for(var key in content)
|
|
|
|
{
|
|
|
|
//var attrs = jQuery.extend({id: key}, content[key], modifications[key]);
|
|
|
|
var attrs = {id: key};
|
|
|
|
var portlet = et2_createWidget('portlet',attrs, this.portlet_container);
|
|
|
|
}
|
|
|
|
this.et2.loadingFinished();
|
|
|
|
|
|
|
|
// Set up sorting of portlets
|
|
|
|
this._do_ordering();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2013-05-23 00:44:27 +02:00
|
|
|
* Add a new portlet from the context menu
|
2013-05-22 22:13:12 +02:00
|
|
|
*/
|
2013-06-05 00:34:21 +02:00
|
|
|
add: function(action, source) {
|
|
|
|
// Put it in the last row, first column, since the mouse position is unknown
|
|
|
|
var max_row = Math.max.apply(null,$j('div',this.portlet_container.div).map(function() {return $j(this).attr('data-row');}));
|
|
|
|
var attrs = {id: this._create_id(), row: max_row + 1, col: 1};
|
|
|
|
|
2013-05-22 22:13:12 +02:00
|
|
|
var portlet = et2_createWidget('portlet',attrs, this.portlet_container);
|
|
|
|
portlet.loadingFinished();
|
|
|
|
|
|
|
|
// Get actual attributes & settings, since they're not available client side yet
|
2013-05-29 21:25:12 +02:00
|
|
|
portlet._process_edit(et2_dialog.OK_BUTTON, {class: action.id});
|
2013-05-22 22:13:12 +02:00
|
|
|
|
|
|
|
// Set up sorting/grid of new portlet
|
|
|
|
var $portlet_container = $j(this.portlet_container.getDOMNode());
|
|
|
|
$portlet_container.data("gridster").add_widget(
|
2013-06-05 00:34:21 +02:00
|
|
|
portlet.getDOMNode(),
|
|
|
|
this.DEFAULT.WIDTH, this.DEFAULT.HEIGHT,
|
|
|
|
attrs.col, attrs.row
|
2013-05-22 22:13:12 +02:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2013-05-23 00:44:27 +02:00
|
|
|
/**
|
|
|
|
* User dropped something on home. Add a new portlet
|
|
|
|
*/
|
|
|
|
add_from_drop: function(action,source,target_action) {
|
2013-05-29 21:25:12 +02:00
|
|
|
|
|
|
|
// Actions got confused drop vs popup
|
|
|
|
if(source[0].id == 'portlets')
|
|
|
|
{
|
|
|
|
return this.add(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
var $portlet_container = $j(this.portlet_container.getDOMNode());
|
|
|
|
|
|
|
|
// Basic portlet attributes
|
|
|
|
var attrs = {id: this._create_id()};
|
|
|
|
|
|
|
|
// Try to find where the drop was
|
|
|
|
if(action != null && action.ui && action.ui.position)
|
|
|
|
{
|
|
|
|
attrs.row = Math.round((action.ui.offset.top - $portlet_container.offset().top )/ this.GRID);
|
|
|
|
attrs.col = Math.max(0,Math.round((action.ui.offset.left - $portlet_container.offset().left) / this.GRID)-1);
|
|
|
|
}
|
|
|
|
|
2013-05-23 00:44:27 +02:00
|
|
|
var portlet = et2_createWidget('portlet',attrs, this.portlet_container);
|
|
|
|
portlet.loadingFinished();
|
|
|
|
|
|
|
|
// Get actual attributes & settings, since they're not available client side yet
|
|
|
|
var drop_data = [];
|
|
|
|
for(var i = 0; i < source.length; i++)
|
|
|
|
{
|
|
|
|
if(source[i].id) drop_data.push(source[i].id);
|
|
|
|
}
|
2013-05-29 21:25:12 +02:00
|
|
|
portlet._process_edit(et2_dialog.OK_BUTTON, {dropped_data: drop_data, class: action.id.substr(5)});
|
2013-05-23 00:44:27 +02:00
|
|
|
|
|
|
|
// Set up sorting/grid of new portlet
|
|
|
|
$portlet_container.data("gridster").add_widget(
|
2013-05-29 21:25:12 +02:00
|
|
|
portlet.getDOMNode(),
|
|
|
|
this.DEFAULT.WIDTH, this.DEFAULT.HEIGHT,
|
|
|
|
attrs.col, attrs.row
|
2013-05-23 00:44:27 +02:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2013-05-22 22:13:12 +02:00
|
|
|
/**
|
|
|
|
* For link_portlet - opens the configured record when the user
|
|
|
|
* double-clicks or chooses view from the context menu
|
|
|
|
*/
|
|
|
|
open_link: function(action) {
|
|
|
|
|
|
|
|
// Get widget
|
|
|
|
var widget = null;
|
|
|
|
while(action.parent != null)
|
|
|
|
{
|
|
|
|
if(action.data && action.data.widget)
|
|
|
|
{
|
|
|
|
widget = action.data.widget;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
action = action.parent;
|
|
|
|
}
|
|
|
|
if(widget == null)
|
|
|
|
{
|
|
|
|
egw().log("warning", "Could not find widget");
|
|
|
|
return;
|
|
|
|
}
|
2013-05-23 00:44:27 +02:00
|
|
|
egw().open(widget.options.settings.entry, "", 'edit');
|
2013-05-22 22:13:12 +02:00
|
|
|
},
|
|
|
|
|
2013-05-29 21:25:12 +02:00
|
|
|
/**
|
|
|
|
* For list_portlet - adds a new link
|
|
|
|
* This is needed here so action system can find it
|
|
|
|
*/
|
|
|
|
add_link: function(action,source,target_action) {
|
|
|
|
this.List.add_link(action, source, target_action);
|
|
|
|
},
|
|
|
|
|
2013-05-22 22:13:12 +02:00
|
|
|
/**
|
|
|
|
* Set up the drag / drop / re-order of portlets
|
|
|
|
*/
|
|
|
|
_do_ordering: function() {
|
|
|
|
var $portlet_container = $j(this.portlet_container.getDOMNode());
|
|
|
|
$portlet_container
|
|
|
|
.addClass("home ui-helper-clearfix")
|
|
|
|
.disableSelection()
|
|
|
|
/* Gridster */
|
|
|
|
.gridster({
|
|
|
|
widget_selector: 'div.et2_portlet',
|
2013-05-29 21:25:12 +02:00
|
|
|
// Dimensions + margins = grid spacing
|
|
|
|
widget_base_dimensions: [this.GRID-5, this.GRID-5],
|
2013-05-22 22:13:12 +02:00
|
|
|
widget_margins: [5,5],
|
|
|
|
extra_rows: 1,
|
|
|
|
extra_cols: 1,
|
|
|
|
min_cols: 3,
|
|
|
|
min_rows: 3,
|
|
|
|
/**
|
|
|
|
* Set which parameters we want when calling serialize().
|
|
|
|
* @param $w jQuery jQuery-wrapped element
|
|
|
|
* @param grid Object Grid settings
|
|
|
|
* @return Object - will be returned by gridster.serialize()
|
|
|
|
*/
|
|
|
|
serialize_params: function($w, grid) {
|
|
|
|
return {
|
|
|
|
id: $w.attr("id"),
|
|
|
|
row: grid.row,
|
|
|
|
col: grid.col,
|
|
|
|
width: grid.width,
|
|
|
|
height: grid.height
|
|
|
|
};
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Gridster's internal drag settings
|
|
|
|
*/
|
|
|
|
draggable: {
|
|
|
|
handle: '.ui-widget-header',
|
|
|
|
stop: function(event,ui) {
|
|
|
|
// Update widget(s)
|
|
|
|
var changed = this.serialize_changed();
|
|
|
|
for (var key in changed)
|
|
|
|
{
|
|
|
|
if(!changed[key].id) continue;
|
2013-09-23 22:01:14 +02:00
|
|
|
// Changed ID is DOM id
|
|
|
|
var widget_id = changed[key].id.substr(window.app.home.et2.getInstanceManager().uniqueId.length + 1);
|
|
|
|
var widget = window.app.home.portlet_container.getWidgetById(widget_id);
|
2013-05-22 22:13:12 +02:00
|
|
|
if(!widget || widget == window.app.home.portlet_container) continue;
|
|
|
|
|
2013-05-29 21:25:12 +02:00
|
|
|
egw().jsonq("home.home_ui.ajax_set_properties",[widget.id, widget.options.settings,{
|
2013-05-22 22:13:12 +02:00
|
|
|
row: changed[key].row,
|
|
|
|
col: changed[key].col
|
|
|
|
}],
|
|
|
|
null,
|
|
|
|
widget, true, widget
|
2013-05-29 21:25:12 +02:00
|
|
|
);
|
2013-05-22 22:13:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// Bind resize to update gridster - this may happen _before_ the widget gets a
|
|
|
|
// chance to update itself, so we can't use the widget
|
|
|
|
$portlet_container
|
|
|
|
.on("resizestop", function(event, ui) {
|
|
|
|
$portlet_container.data("gridster").resize_widget(
|
|
|
|
ui.element,
|
2013-06-10 17:05:21 +02:00
|
|
|
Math.round(ui.size.width / app.home.GRID),
|
|
|
|
Math.round(ui.size.height / app.home.GRID)
|
2013-05-22 22:13:12 +02:00
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an ID that should be unique, at least amoung a single user's portlets
|
|
|
|
*/
|
|
|
|
_create_id: function() {
|
|
|
|
var id = '';
|
|
|
|
do
|
|
|
|
{
|
|
|
|
id = Math.floor((1 + Math.random()) * 0x10000)
|
|
|
|
.toString(16)
|
|
|
|
.substring(1);
|
|
|
|
}
|
|
|
|
while(this.portlet_container.getWidgetById(id));
|
|
|
|
return id;
|
2013-05-29 21:25:12 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Functions for the list portlet
|
|
|
|
*/
|
|
|
|
List:
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* List uses mostly JS to generate its content, so we just do it on the JS side by
|
|
|
|
* returning a call to this function as the HTML content.
|
|
|
|
*
|
|
|
|
* @param id String The ID of the portlet
|
|
|
|
* @param list_values Array List of information passed to the link widget
|
|
|
|
*/
|
|
|
|
set_content: function(id, list_values)
|
|
|
|
{
|
2013-09-18 01:24:56 +02:00
|
|
|
try {
|
|
|
|
var portlet = app.home.portlet_container.getWidgetById(id);
|
|
|
|
} catch(e) {
|
|
|
|
egw.debug("log", "Tried to set home list content with no etemplate");
|
|
|
|
return;
|
|
|
|
};
|
2013-09-18 00:27:29 +02:00
|
|
|
if(portlet != null)
|
|
|
|
{
|
|
|
|
var list = portlet.getWidgetById(id+'-list');
|
|
|
|
if(list)
|
|
|
|
{
|
2013-05-29 21:25:12 +02:00
|
|
|
// List was just rudely pulled from DOM by the call to HTML, put it back
|
2013-09-18 00:27:29 +02:00
|
|
|
portlet.content.append(list.getDOMNode());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-05-29 21:25:12 +02:00
|
|
|
// Create widget
|
2013-09-18 00:27:29 +02:00
|
|
|
list = et2_createWidget('link-list', {id: id+'-list'}, portlet);
|
2013-05-29 21:25:12 +02:00
|
|
|
list.doLoadingFinished();
|
2013-09-18 00:27:29 +02:00
|
|
|
// Abuse link list by overwriting delete handler
|
|
|
|
list._delete_link = app.home.List.delete_link;
|
|
|
|
}
|
2013-09-18 01:24:56 +02:00
|
|
|
list.set_value(list_values);
|
2013-05-29 21:25:12 +02:00
|
|
|
|
2013-09-18 01:24:56 +02:00
|
|
|
// Disable link list context menu
|
|
|
|
$j('tr',list.list).unbind('contextmenu');
|
2013-05-29 21:25:12 +02:00
|
|
|
|
2013-09-18 01:24:56 +02:00
|
|
|
// Allow scroll bars
|
|
|
|
portlet.content.css('overflow', 'auto');
|
2013-09-18 00:27:29 +02:00
|
|
|
}
|
2013-05-29 21:25:12 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* For list_portlet - opens a dialog to add a new entry to the list
|
|
|
|
*/
|
|
|
|
add_link: function(action, source, target_action) {
|
|
|
|
// Actions got confused drop vs popup
|
|
|
|
if(source[0].id == 'portlets')
|
|
|
|
{
|
|
|
|
return this.add_link(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get widget
|
|
|
|
var widget = null;
|
|
|
|
while(action.parent != null)
|
|
|
|
{
|
|
|
|
if(action.data && action.data.widget)
|
|
|
|
{
|
|
|
|
widget = action.data.widget;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
action = action.parent;
|
|
|
|
}
|
2013-06-03 22:58:28 +02:00
|
|
|
if(target_action == null)
|
2013-05-29 21:25:12 +02:00
|
|
|
{
|
2013-10-07 18:53:13 +02:00
|
|
|
var link = et2_createWidget('link-entry', {label: this.egw.lang('Add')}, this.portlet_container);
|
2013-05-29 21:25:12 +02:00
|
|
|
var dialog = et2_dialog.show_dialog(
|
|
|
|
function(button_id) {
|
2013-09-18 00:27:29 +02:00
|
|
|
if(button_id == et2_dialog.CANCEL_BUTTON) return;
|
2013-07-04 22:03:23 +02:00
|
|
|
var new_list = widget.options.settings.list || [];
|
2013-09-18 00:27:29 +02:00
|
|
|
var add = link.getValue();
|
2013-05-29 21:25:12 +02:00
|
|
|
link.destroy();
|
2013-09-18 00:27:29 +02:00
|
|
|
for(var i = 0; i < new_list.length; i++)
|
|
|
|
{
|
|
|
|
if(new_list[i].app == add.app && new_list[i].id == add.id)
|
|
|
|
{
|
|
|
|
// Duplicate
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
new_list.push(add);
|
|
|
|
widget._process_edit(button_id,{list: new_list});
|
2013-05-29 21:25:12 +02:00
|
|
|
},
|
|
|
|
'Add',
|
2013-10-07 18:53:13 +02:00
|
|
|
this.egw.lang('Add'), {},
|
2013-05-29 21:25:12 +02:00
|
|
|
et2_dialog.BUTTONS_OK_CANCEL
|
|
|
|
);
|
|
|
|
dialog.set_message(link.getDOMNode());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Drag'n'dropped something on the list - just send action IDs
|
|
|
|
var drop_data = [];
|
|
|
|
for(var i = 0; i < source.length; i++)
|
|
|
|
{
|
|
|
|
if(source[i].id) drop_data.push(source[i].id);
|
|
|
|
}
|
|
|
|
widget._process_edit(et2_dialog.BUTTONS_OK_CANCEL,{
|
|
|
|
list: widget.options.settings.list || {},
|
|
|
|
dropped_data: drop_data
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a link from the list
|
|
|
|
*/
|
|
|
|
delete_link: function(undef, row) {
|
|
|
|
// Quick response
|
|
|
|
row.slideUp(row.remove);
|
|
|
|
// Actual removal
|
|
|
|
this._parent.options.settings.list.splice(row.index(), 1);
|
|
|
|
this._parent._process_edit(et2_dialog.OK_BUTTON,{list: this._parent.options.settings.list || {}});
|
|
|
|
}
|
2013-05-22 22:13:12 +02:00
|
|
|
}
|
|
|
|
});
|