2011-03-24 18:06:44 +01:00
|
|
|
/**
|
|
|
|
* eGroupWare egw_action framework - egw action framework
|
|
|
|
*
|
|
|
|
* @link http://www.egroupware.org
|
|
|
|
* @author Andreas Stöckel <as@stylite.de>
|
|
|
|
* @copyright 2011 by Andreas Stöckel
|
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
* @package egw_action
|
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
|
2011-08-03 16:04:30 +02:00
|
|
|
/*egw:uses
|
|
|
|
egw_action;
|
|
|
|
egw_action_common;
|
|
|
|
egw_action_popup;
|
|
|
|
jquery.jquery;
|
|
|
|
jquery.jquery-ui;
|
|
|
|
*/
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Register the drag and drop handlers
|
|
|
|
*/
|
|
|
|
if (typeof window._egwActionClasses == "undefined")
|
|
|
|
window._egwActionClasses = {}
|
|
|
|
_egwActionClasses["drag"] = {
|
|
|
|
"actionConstructor": egwDragAction,
|
|
|
|
"implementation": getDragImplementation
|
|
|
|
}
|
|
|
|
_egwActionClasses["drop"] = {
|
|
|
|
"actionConstructor": egwDropAction,
|
|
|
|
"implementation": getDropImplementation
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The egwDragAction class overwrites the egwAction class and adds the new
|
|
|
|
* "dragType" propery. The "onExecute" event of the drag action will be called
|
|
|
|
* whenever dragging starts. The onExecute JS handler should return the
|
|
|
|
* drag-drop helper object - otherwise an default helper will be generated.
|
|
|
|
*/
|
|
|
|
function egwDragAction(_id, _handler, _caption, _icon, _onExecute, _allowOnMultiple)
|
|
|
|
{
|
|
|
|
var action = new egwAction(_id, _handler, _caption, _icon, _onExecute, _allowOnMultiple);
|
|
|
|
|
|
|
|
action.type = "drag";
|
|
|
|
action.dragType = "default";
|
|
|
|
action.hideOnDisabled = true;
|
|
|
|
|
|
|
|
action.set_dragType = function(_value) {
|
|
|
|
action.dragType = _value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var
|
|
|
|
_dragActionImpl = null;
|
|
|
|
|
|
|
|
function getDragImplementation()
|
|
|
|
{
|
|
|
|
if (!_dragActionImpl)
|
|
|
|
{
|
|
|
|
_dragActionImpl = new egwDragActionImplementation();
|
|
|
|
}
|
|
|
|
return _dragActionImpl
|
|
|
|
}
|
|
|
|
|
|
|
|
function egwDragActionImplementation()
|
|
|
|
{
|
|
|
|
var ai = new egwActionImplementation();
|
|
|
|
|
|
|
|
ai.type = "drag";
|
|
|
|
|
|
|
|
ai.helper = null;
|
|
|
|
ai.ddTypes = [];
|
|
|
|
ai.selected = [];
|
|
|
|
|
|
|
|
ai.doRegisterAction = function(_aoi, _callback, _context)
|
|
|
|
{
|
|
|
|
var node = _aoi.getDOMNode();
|
|
|
|
|
|
|
|
if (node)
|
|
|
|
{
|
2011-03-25 14:12:24 +01:00
|
|
|
// Prevent selection
|
|
|
|
node.onselectstart = function () {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2011-07-03 11:00:36 +02:00
|
|
|
$j(node).draggable(
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
"distance": 20,
|
|
|
|
"cursor": "move",
|
|
|
|
"cursorAt": { top: -12, left: -12 },
|
|
|
|
"helper": function(e) {
|
|
|
|
// The helper function is called before the start function
|
|
|
|
// is evoked. Call the given callback function. The callback
|
|
|
|
// function will gather the selected elements and action links
|
|
|
|
// and call the doExecuteImplementation function. This
|
|
|
|
// will call the onExecute function of the first action
|
|
|
|
// in order to obtain the helper object (stored in ai.helper)
|
|
|
|
// and the multiple dragDropTypes (ai.ddTypes)
|
|
|
|
_callback.call(_context, false, ai);
|
|
|
|
|
2011-07-03 11:00:36 +02:00
|
|
|
$j(node).data("ddTypes", ai.ddTypes);
|
|
|
|
$j(node).data("selected", ai.selected);
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2011-03-25 14:12:24 +01:00
|
|
|
if (ai.helper)
|
|
|
|
{
|
|
|
|
// Append the helper object to the body element - this
|
|
|
|
// fixes a bug in IE: If the element isn't inserted into
|
|
|
|
// the DOM-tree jquery appends it to the parent node.
|
|
|
|
// In case this is a table it doesn't work correctly
|
2011-07-03 11:00:36 +02:00
|
|
|
$j("body").append(ai.helper);
|
2011-03-25 14:12:24 +01:00
|
|
|
return ai.helper;
|
|
|
|
}
|
|
|
|
|
2011-03-24 18:06:44 +01:00
|
|
|
// Return an empty div if the helper dom node is not set
|
2011-07-03 11:00:36 +02:00
|
|
|
return $j(document.createElement("div"));
|
2011-03-24 18:06:44 +01:00
|
|
|
},
|
|
|
|
"start": function(e) {
|
|
|
|
return ai.helper != null;
|
|
|
|
},
|
2011-03-25 14:12:24 +01:00
|
|
|
// Solves problem with scroll position changing in the grid
|
2011-03-24 19:17:27 +01:00
|
|
|
// component
|
|
|
|
"refreshPositions": true,
|
2011-03-25 14:12:24 +01:00
|
|
|
"scroll": false,
|
2011-05-28 17:24:31 +02:00
|
|
|
"containment": "document",
|
|
|
|
"iframeFix": true
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ai.doUnregisterAction = function(_aoi)
|
|
|
|
{
|
2011-06-26 16:57:05 +02:00
|
|
|
var node = _aoi.getDOMNode();
|
|
|
|
|
|
|
|
if (node) {
|
2011-07-03 11:00:36 +02:00
|
|
|
$j(node).draggable("destroy");
|
2011-06-26 16:57:05 +02:00
|
|
|
}
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the context menu and shows it at the given position/DOM-Node.
|
|
|
|
*/
|
|
|
|
ai.doExecuteImplementation = function(_context, _selected, _links)
|
|
|
|
{
|
|
|
|
// Reset the helper object of the action implementation
|
|
|
|
this.helper = null;
|
|
|
|
var hasLink = false;
|
|
|
|
|
|
|
|
// Store the drag-drop types
|
|
|
|
this.ddTypes = [];
|
|
|
|
this.selected = _selected;
|
|
|
|
|
|
|
|
// Call the onExecute event of the first actionObject
|
2011-04-23 11:07:31 +02:00
|
|
|
for (var k in _links)
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
if (_links[k].visible)
|
|
|
|
{
|
|
|
|
hasLink = true;
|
|
|
|
|
|
|
|
// Only execute the following code if a JS function is registered
|
|
|
|
// for the action and this is the first action link
|
2011-05-07 13:22:23 +02:00
|
|
|
if (!this.helper && _links[k].actionObj.onExecute.hasHandler())
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
this.helper = _links[k].actionObj.execute(_selected);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push the dragType of the associated action object onto the
|
|
|
|
// drag type list - this allows an element to support multiple
|
|
|
|
// drag/drop types.
|
|
|
|
var type = _links[k].actionObj.dragType;
|
|
|
|
if (this.ddTypes.indexOf(type) == -1)
|
|
|
|
{
|
|
|
|
this.ddTypes.push(_links[k].actionObj.dragType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no helper has been defined, create an default one
|
|
|
|
if (!this.helper && hasLink)
|
|
|
|
{
|
2011-07-03 11:00:36 +02:00
|
|
|
this.helper = $j(document.createElement("div"));
|
2011-03-24 18:06:44 +01:00
|
|
|
this.helper.addClass("egw_action_ddHelper");
|
|
|
|
this.helper.text("(" + _selected.length + ")");
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ai;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The egwDropAction class overwrites the egwAction class and adds the "acceptedTypes"
|
|
|
|
* property. This array should contain all "dragTypes" the drop action is allowed
|
|
|
|
* to
|
|
|
|
*/
|
|
|
|
function egwDropAction(_id, _handler, _caption, _icon, _onExecute, _allowOnMultiple)
|
|
|
|
{
|
|
|
|
var action = new egwAction(_id, _handler, _caption, _icon, _onExecute, _allowOnMultiple);
|
|
|
|
|
|
|
|
action.type = "drop";
|
|
|
|
action.acceptedTypes = ["default"];
|
|
|
|
action.canHaveChildren = ["drag","popup"];
|
|
|
|
action["default"] = false;
|
|
|
|
action.order = 0;
|
|
|
|
action.group = 0;
|
|
|
|
|
|
|
|
action.set_default = function(_value) {
|
|
|
|
action["default"] = _value;
|
|
|
|
}
|
|
|
|
|
|
|
|
action.set_order = function(_value) {
|
|
|
|
action.order = _value;
|
|
|
|
}
|
|
|
|
|
|
|
|
action.set_group = function(_value) {
|
|
|
|
action.group = _value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The acceptType property allows strings as well as arrays - strings are
|
|
|
|
* automatically included in an array.
|
|
|
|
*/
|
|
|
|
action.set_acceptedTypes = function(_value) {
|
|
|
|
if (_value instanceof Array)
|
|
|
|
{
|
|
|
|
action.acceptedTypes = _value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
action.acceptedTypes = [_value];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
var
|
|
|
|
_dropActionImpl = null;
|
|
|
|
|
|
|
|
function getDropImplementation()
|
|
|
|
{
|
|
|
|
if (!_dropActionImpl)
|
|
|
|
{
|
|
|
|
_dropActionImpl = new egwDropActionImplementation();
|
|
|
|
}
|
|
|
|
return _dropActionImpl
|
|
|
|
}
|
|
|
|
|
|
|
|
var EGW_AI_DRAG = 0x0100; // Use the first byte as mask for event types - 01 is for events used with drag stuff
|
|
|
|
var EGW_AI_DRAG_OUT = EGW_AI_DRAG | 0x01;
|
|
|
|
var EGW_AI_DRAG_OVER = EGW_AI_DRAG | 0x02;
|
|
|
|
|
|
|
|
function egwDropActionImplementation()
|
|
|
|
{
|
|
|
|
var ai = new egwActionImplementation();
|
|
|
|
|
|
|
|
ai.type = "drop";
|
|
|
|
|
|
|
|
ai.doRegisterAction = function(_aoi, _callback, _context)
|
|
|
|
{
|
|
|
|
var node = _aoi.getDOMNode();
|
2011-06-26 16:57:05 +02:00
|
|
|
var self = this;
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
if (node)
|
|
|
|
{
|
2011-07-03 11:00:36 +02:00
|
|
|
$j(node).droppable(
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
"accept": function(_draggable) {
|
2011-06-19 13:15:37 +02:00
|
|
|
if (typeof _draggable.data("ddTypes") != "undefined")
|
|
|
|
{
|
2011-06-26 16:57:05 +02:00
|
|
|
var accepted = self._fetchAccepted(
|
|
|
|
_callback.call(_context, "links", self, EGW_AO_EXEC_THIS));
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2011-06-19 13:15:37 +02:00
|
|
|
// Check whether all drag types of the selected objects
|
|
|
|
// are accepted
|
|
|
|
var ddTypes = _draggable.data("ddTypes");
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2011-06-19 13:15:37 +02:00
|
|
|
for (var i = 0; i < ddTypes.length; i++)
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
2011-06-19 13:15:37 +02:00
|
|
|
if (accepted.indexOf(ddTypes[i]) == -1)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
|
2011-06-19 13:15:37 +02:00
|
|
|
return true;
|
|
|
|
}
|
2011-03-24 18:06:44 +01:00
|
|
|
},
|
|
|
|
"drop": function(event, ui) {
|
|
|
|
var draggable = ui.draggable;
|
|
|
|
var ddTypes = draggable.data("ddTypes");
|
|
|
|
var selected = draggable.data("selected");
|
|
|
|
|
2011-06-26 16:57:05 +02:00
|
|
|
var links = _callback.call(_context, "links", self, EGW_AO_EXEC_THIS);
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
// Disable all links which only accept types which are not
|
|
|
|
// inside ddTypes
|
2011-04-23 11:07:31 +02:00
|
|
|
for (var k in links)
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
var accepted = links[k].actionObj.acceptedTypes;
|
|
|
|
|
|
|
|
for (var i = 0; i < ddTypes.length; i++)
|
|
|
|
{
|
|
|
|
if (accepted.indexOf(ddTypes[i]) == -1)
|
|
|
|
{
|
|
|
|
links[k].enabled = false;
|
|
|
|
if (links[k].actionObj.hideOnDisabled)
|
|
|
|
{
|
|
|
|
links[k].visible = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check whether there is only one link
|
|
|
|
var cnt = 0;
|
|
|
|
var lnk = null;
|
2011-04-23 11:07:31 +02:00
|
|
|
for (var k in links)
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
if (links[k].enabled && links[k].visible)
|
|
|
|
{
|
|
|
|
lnk = links[k];
|
|
|
|
cnt += 1 + links[k].actionObj.children.length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cnt == 1)
|
|
|
|
{
|
2011-07-21 12:50:33 +02:00
|
|
|
window.setTimeout(function() {
|
|
|
|
lnk.actionObj.execute(selected, _context);
|
|
|
|
},0);
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cnt > 1)
|
|
|
|
{
|
|
|
|
// More than one drop action link is associated
|
|
|
|
// to the drop event - show those as a popup menu
|
|
|
|
// and let the user decide which one to use.
|
|
|
|
// This is possible as the popup and the popup action
|
|
|
|
// object and the drop action object share same
|
|
|
|
// set of properties.
|
|
|
|
var popup = getPopupImplementation();
|
|
|
|
var pos = popup._getPageXY(event);
|
2011-03-25 14:12:24 +01:00
|
|
|
window.setTimeout(function() {
|
|
|
|
popup.doExecuteImplementation(pos, selected, links,
|
|
|
|
_context);
|
|
|
|
}, 0); // Timeout is needed to have it working in IE
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_aoi.triggerEvent(EGW_AI_DRAG_OUT);
|
|
|
|
},
|
|
|
|
"over": function() {
|
|
|
|
_aoi.triggerEvent(EGW_AI_DRAG_OVER);
|
|
|
|
},
|
|
|
|
"out": function() {
|
|
|
|
_aoi.triggerEvent(EGW_AI_DRAG_OUT);
|
|
|
|
},
|
|
|
|
"tolerance": "pointer"
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ai.doUnregisterAction = function(_aoi)
|
|
|
|
{
|
2011-06-26 16:57:05 +02:00
|
|
|
var node = _aoi.getDOMNode();
|
|
|
|
|
|
|
|
if (node) {
|
2011-07-03 11:00:36 +02:00
|
|
|
$j(node).droppable("destroy");
|
2011-06-26 16:57:05 +02:00
|
|
|
}
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ai._fetchAccepted = function(_links)
|
|
|
|
{
|
|
|
|
// Accumulate the accepted types
|
|
|
|
var accepted = [];
|
2011-04-23 11:07:31 +02:00
|
|
|
for (var k in _links)
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
for (var i = 0; i < _links[k].actionObj.acceptedTypes.length; i++)
|
|
|
|
{
|
|
|
|
var type = _links[k].actionObj.acceptedTypes[i];
|
|
|
|
|
|
|
|
if (accepted.indexOf(type) == -1)
|
|
|
|
{
|
|
|
|
accepted.push(type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return accepted;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the context menu and shows it at the given position/DOM-Node.
|
|
|
|
*/
|
|
|
|
ai.doExecuteImplementation = function(_context, _selected, _links)
|
|
|
|
{
|
|
|
|
if (_context == "links")
|
|
|
|
{
|
|
|
|
return _links;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ai;
|
|
|
|
}
|
|
|
|
|