2011-02-26 20:21:55 +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
|
2016-06-06 17:38:20 +02:00
|
|
|
vendor.bower-asset.jquery.dist.jquery;
|
2011-08-03 16:04:30 +02:00
|
|
|
egw_menu;
|
|
|
|
*/
|
|
|
|
|
2021-06-09 11:11:34 +02:00
|
|
|
import {egwAction, egwActionImplementation, egwActionObject} from './egw_action.js';
|
|
|
|
import {egwFnct} from './egw_action_common.js';
|
2021-06-08 14:11:59 +02:00
|
|
|
import {egwMenu, _egw_active_menu} from "./egw_menu.js";
|
2021-06-14 13:45:57 +02:00
|
|
|
import {EGW_KEY_ENTER, EGW_KEY_MENU} from "./egw_action_constants.js";
|
2022-05-25 17:40:42 +02:00
|
|
|
import {tapAndSwipe} from "../tapandswipe";
|
2021-06-08 14:11:59 +02:00
|
|
|
|
2011-02-26 20:21:55 +01:00
|
|
|
if (typeof window._egwActionClasses == "undefined")
|
2014-10-24 14:44:15 +02:00
|
|
|
window._egwActionClasses = {};
|
2011-02-26 20:21:55 +01:00
|
|
|
_egwActionClasses["popup"] = {
|
|
|
|
"actionConstructor": egwPopupAction,
|
|
|
|
"implementation": getPopupImplementation
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
|
2021-06-08 14:11:59 +02:00
|
|
|
export function egwPopupAction(_id, _handler, _caption, _icon, _onExecute, _allowOnMultiple)
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
|
|
|
var action = new egwAction(_id, _handler, _caption, _icon, _onExecute, _allowOnMultiple);
|
|
|
|
action.type = "popup";
|
2011-03-23 15:05:39 +01:00
|
|
|
action.canHaveChildren = ["popup"];
|
2011-02-26 20:21:55 +01:00
|
|
|
action["default"] = false;
|
|
|
|
action.order = 0;
|
|
|
|
action.group = 0;
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
action.hint = false;
|
|
|
|
action.checkbox = false;
|
|
|
|
action.radioGroup = 0;
|
|
|
|
action.checked = false;
|
2019-03-12 18:33:17 +01:00
|
|
|
action.confirm_mass_selection = null;
|
2011-06-16 22:43:33 +02:00
|
|
|
action.shortcut = null;
|
2020-05-27 11:54:50 +02:00
|
|
|
action.singleClick = false;
|
|
|
|
|
|
|
|
action.set_singleClick = function(_value) {
|
|
|
|
action["singleClick"] = _value;
|
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
|
|
|
|
action.set_default = function(_value) {
|
|
|
|
action["default"] = _value;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
|
|
|
|
action.set_order = function(_value) {
|
|
|
|
action.order = _value;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
|
|
|
|
action.set_group = function(_value) {
|
|
|
|
action.group = _value;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
action.set_hint = function(_value) {
|
|
|
|
action.hint = _value;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
|
|
|
|
// If true, the action will be rendered as checkbox
|
|
|
|
action.set_checkbox = function(_value) {
|
|
|
|
action.checkbox = _value;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
|
|
|
|
action.set_checked = function(_value) {
|
|
|
|
action.checked = _value;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
|
2019-03-12 18:33:17 +01:00
|
|
|
/**
|
|
|
|
* Set either a confirmation prompt, or TRUE to indicate that this action
|
|
|
|
* cares about large selections and to ask the confirmation prompt(s)
|
2019-07-31 19:21:36 +02:00
|
|
|
*
|
2019-03-12 18:33:17 +01:00
|
|
|
* @param {String|Boolean} _value
|
|
|
|
*/
|
|
|
|
action.set_confirm_mass_selection = function(_value) {
|
|
|
|
action.confirm_mass_selection = _value;
|
|
|
|
};
|
|
|
|
|
2016-04-28 18:39:53 +02:00
|
|
|
// Allow checkbox to be set from context using the given function
|
|
|
|
action.set_isChecked = function(_value) {
|
|
|
|
action.isChecked = new egwFnct(this, null, []);
|
|
|
|
if(_value !== null)
|
|
|
|
{
|
|
|
|
action.isChecked.setValue(_value);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
// If radioGroup is >0 and the element is a checkbox, radioGroup specifies
|
|
|
|
// the group of radio buttons this one belongs to
|
|
|
|
action.set_radioGroup = function(_value) {
|
|
|
|
action.radioGroup = _value;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
|
2011-06-16 22:43:33 +02:00
|
|
|
action.set_shortcut = function(_value) {
|
2011-06-19 12:48:51 +02:00
|
|
|
if (_value)
|
2011-06-16 22:43:33 +02:00
|
|
|
{
|
2011-06-19 12:48:51 +02:00
|
|
|
var sc = {
|
|
|
|
"keyCode": -1,
|
|
|
|
"shift": false,
|
|
|
|
"ctrl": false,
|
|
|
|
"alt": false
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-06-19 12:48:51 +02:00
|
|
|
|
2013-10-24 19:01:58 +02:00
|
|
|
if (typeof _value == "object" && typeof _value.keyCode != "undefined" &&
|
|
|
|
typeof _value.caption != "undefined")
|
2011-06-19 12:48:51 +02:00
|
|
|
{
|
|
|
|
sc.keyCode = _value.keyCode;
|
2011-06-25 16:27:30 +02:00
|
|
|
sc.caption = _value.caption;
|
2011-06-19 12:48:51 +02:00
|
|
|
sc.shift = (typeof _value.shift == "undefined") ? false : _value.shift;
|
|
|
|
sc.ctrl = (typeof _value.ctrl == "undefined") ? false : _value.ctrl;
|
|
|
|
sc.alt = (typeof _value.alt == "undefined") ? false : _value.alt;
|
|
|
|
}
|
2011-06-16 22:43:33 +02:00
|
|
|
|
2011-06-19 12:48:51 +02:00
|
|
|
this.shortcut = sc;
|
|
|
|
}
|
|
|
|
else
|
2011-06-16 22:43:33 +02:00
|
|
|
{
|
2011-06-19 12:48:51 +02:00
|
|
|
this.shortcut = false;
|
2011-06-16 22:43:33 +02:00
|
|
|
}
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-06-16 22:43:33 +02:00
|
|
|
|
2011-02-26 20:21:55 +01:00
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
var
|
|
|
|
_popupActionImpl = null;
|
|
|
|
|
2021-06-08 14:11:59 +02:00
|
|
|
export function getPopupImplementation()
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
|
|
|
if (!_popupActionImpl)
|
|
|
|
{
|
|
|
|
_popupActionImpl = new egwPopupActionImplementation();
|
|
|
|
}
|
2014-10-24 14:44:15 +02:00
|
|
|
return _popupActionImpl;
|
2011-02-26 20:21:55 +01:00
|
|
|
}
|
|
|
|
|
2021-06-08 14:11:59 +02:00
|
|
|
export function egwPopupActionImplementation()
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
|
|
|
var ai = new egwActionImplementation();
|
|
|
|
|
|
|
|
ai.type = "popup";
|
|
|
|
|
2014-11-19 18:59:16 +01:00
|
|
|
ai.auto_paste = true;
|
|
|
|
|
2011-06-03 00:53:23 +02:00
|
|
|
/**
|
|
|
|
* Registers the handler for the default action
|
2014-10-24 14:44:15 +02:00
|
|
|
*
|
|
|
|
* @param {DOMNode} _node
|
|
|
|
* @param {function} _callback
|
|
|
|
* @param {object} _context
|
|
|
|
* @returns {boolean}
|
2011-06-03 00:53:23 +02:00
|
|
|
*/
|
2014-10-24 14:44:15 +02:00
|
|
|
ai._registerDefault = function(_node, _callback, _context)
|
|
|
|
{
|
2011-06-03 00:53:23 +02:00
|
|
|
var defaultHandler = function(e) {
|
2015-03-31 12:01:51 +02:00
|
|
|
// Prevent bubbling bound event on <a> tag, on touch devices
|
|
|
|
// a tag should be handled by default event
|
2016-03-31 12:05:39 +02:00
|
|
|
if (egwIsMobile() && e.target.tagName == "A") return true;
|
2015-11-02 09:26:17 +01:00
|
|
|
|
2011-06-03 00:53:23 +02:00
|
|
|
if (typeof document.selection != "undefined" && typeof document.selection.empty != "undefined")
|
|
|
|
{
|
|
|
|
document.selection.empty();
|
|
|
|
}
|
|
|
|
else if( typeof window.getSelection != "undefined")
|
|
|
|
{
|
|
|
|
var sel = window.getSelection();
|
|
|
|
sel.removeAllRanges();
|
|
|
|
}
|
2011-02-26 20:21:55 +01:00
|
|
|
|
2020-05-27 12:43:21 +02:00
|
|
|
if (!(_context.manager.getActionsByAttr('singleClick', true).length > 0 &&
|
|
|
|
e.originalEvent.target.classList.contains('et2_clickable')))
|
|
|
|
{
|
|
|
|
_callback.call(_context, "default", ai);
|
|
|
|
}
|
|
|
|
|
2015-01-12 17:17:29 +01:00
|
|
|
// Stop action from bubbling up to parents
|
|
|
|
e.stopPropagation();
|
|
|
|
e.cancelBubble = true;
|
2015-02-16 16:18:25 +01:00
|
|
|
|
2015-02-19 12:45:05 +01:00
|
|
|
// remove context menu if we are in mobile theme
|
|
|
|
// and intended to open the entry
|
|
|
|
if (_egw_active_menu && e.which == 1) _egw_active_menu.hide();
|
2011-06-03 00:53:23 +02:00
|
|
|
return false;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-03-14 21:11:08 +01:00
|
|
|
|
2020-05-27 11:54:50 +02:00
|
|
|
if (egwIsMobile() || _context.manager.getActionsByAttr('singleClick', true).length > 0) {
|
2016-06-02 16:51:15 +02:00
|
|
|
jQuery(_node).bind('click', defaultHandler);
|
2011-06-03 00:53:23 +02:00
|
|
|
} else {
|
2011-06-03 13:21:36 +02:00
|
|
|
_node.ondblclick = defaultHandler;
|
2011-06-03 00:53:23 +02:00
|
|
|
}
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-03-14 21:11:08 +01:00
|
|
|
|
2011-06-16 15:43:46 +02:00
|
|
|
ai._getDefaultLink = function(_links) {
|
|
|
|
var defaultAction = null;
|
|
|
|
for (var k in _links)
|
|
|
|
{
|
|
|
|
if (_links[k].actionObj["default"] && _links[k].enabled)
|
|
|
|
{
|
|
|
|
defaultAction = _links[k].actionObj;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return defaultAction;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-06-16 15:43:46 +02:00
|
|
|
|
2011-06-19 12:48:51 +02:00
|
|
|
ai._searchShortcut = function (_key, _objs, _links) {
|
|
|
|
for (var i = 0; i < _objs.length; i++)
|
|
|
|
{
|
|
|
|
var sc = _objs[i].shortcut;
|
|
|
|
if (sc && sc.keyCode == _key.keyCode && sc.shift == _key.shift &&
|
|
|
|
sc.ctrl == _key.ctrl && sc.alt == _key.alt &&
|
|
|
|
_objs[i].type == "popup" && (typeof _links[_objs[i].id] == "undefined" ||
|
|
|
|
_links[_objs[i].id].enabled))
|
|
|
|
{
|
|
|
|
return _objs[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
var obj = this._searchShortcut(_key, _objs[i].children, _links);
|
|
|
|
if (obj) {
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
}
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-06-19 12:48:51 +02:00
|
|
|
|
|
|
|
ai._searchShortcutInLinks = function(_key, _links) {
|
|
|
|
var objs = [];
|
|
|
|
for (var k in _links)
|
|
|
|
{
|
|
|
|
if (_links[k].enabled)
|
|
|
|
{
|
|
|
|
objs.push(_links[k].actionObj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ai._searchShortcut(_key, objs, _links);
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-06-19 12:48:51 +02:00
|
|
|
|
2011-06-16 15:43:46 +02:00
|
|
|
/**
|
|
|
|
* Handles a key press
|
2014-10-24 14:44:15 +02:00
|
|
|
*
|
|
|
|
* @param {object} _key
|
|
|
|
* @param {type} _selected
|
|
|
|
* @param {type} _links
|
|
|
|
* @param {type} _target
|
|
|
|
* @returns {Boolean}
|
2011-06-16 15:43:46 +02:00
|
|
|
*/
|
|
|
|
ai._handleKeyPress = function(_key, _selected, _links, _target) {
|
2011-06-19 12:48:51 +02:00
|
|
|
// Handle the default
|
2011-06-16 15:43:46 +02:00
|
|
|
if (_key.keyCode == EGW_KEY_ENTER && !_key.ctrl && !_key.shift && !_key.alt) {
|
|
|
|
var defaultAction = this._getDefaultLink(_links);
|
|
|
|
if (defaultAction)
|
|
|
|
{
|
|
|
|
defaultAction.execute(_selected);
|
2011-06-19 12:48:51 +02:00
|
|
|
return true;
|
2011-06-16 15:43:46 +02:00
|
|
|
}
|
|
|
|
}
|
2011-06-19 12:48:51 +02:00
|
|
|
|
2013-04-09 18:13:10 +02:00
|
|
|
// Menu button
|
|
|
|
if (_key.keyCode == EGW_KEY_MENU && !_key.ctrl)
|
|
|
|
{
|
|
|
|
return this.doExecuteImplementation({posx:0,posy:0}, _selected, _links, _target);
|
|
|
|
}
|
2014-10-24 14:44:15 +02:00
|
|
|
|
2013-04-09 18:13:10 +02:00
|
|
|
|
2011-06-19 12:48:51 +02:00
|
|
|
// Check whether the given shortcut exists
|
|
|
|
var obj = this._searchShortcutInLinks(_key, _links);
|
|
|
|
if (obj)
|
|
|
|
{
|
|
|
|
obj.execute(_selected);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2021-07-29 14:57:10 +02:00
|
|
|
ai._handleTapHold = function (_node, _callback)
|
|
|
|
{
|
2021-08-04 11:50:43 +02:00
|
|
|
//TODO (todo-jquery): ATM we need to convert the possible given jquery dom node object into DOM Element, this
|
|
|
|
// should be no longer neccessary after removing jQuery nodes.
|
|
|
|
if (_node instanceof jQuery)
|
|
|
|
{
|
|
|
|
_node = _node[0];
|
|
|
|
}
|
2021-07-29 14:57:10 +02:00
|
|
|
|
2022-05-25 17:40:42 +02:00
|
|
|
let tap = new tapAndSwipe(_node, {
|
|
|
|
// this threshold must be the same as the one set in et2_dataview_view_aoi
|
|
|
|
tapHoldThreshold: 600,
|
2022-05-30 16:07:26 +02:00
|
|
|
allowScrolling: "both",
|
|
|
|
tapAndHold: function(event, fingercount)
|
2022-05-25 17:40:42 +02:00
|
|
|
{
|
2022-05-30 16:07:26 +02:00
|
|
|
if (fingercount >= 2) return;
|
2022-05-25 17:40:42 +02:00
|
|
|
// don't trigger contextmenu if sorting is happening
|
|
|
|
if (document.querySelector('.sortable-drag')) return;
|
|
|
|
|
|
|
|
_callback(event);
|
|
|
|
}
|
2021-07-29 14:57:10 +02:00
|
|
|
});
|
|
|
|
}
|
2011-06-03 00:53:23 +02:00
|
|
|
/**
|
|
|
|
* Registers the handler for the context menu
|
2014-10-24 14:44:15 +02:00
|
|
|
*
|
|
|
|
* @param {DOMNode} _node
|
|
|
|
* @param {function} _callback
|
|
|
|
* @param {object} _context
|
|
|
|
* @returns {boolean}
|
2011-06-03 00:53:23 +02:00
|
|
|
*/
|
2014-10-24 14:44:15 +02:00
|
|
|
ai._registerContext = function(_node, _callback, _context)
|
|
|
|
{
|
2011-06-03 00:53:23 +02:00
|
|
|
var contextHandler = function(e) {
|
2016-03-09 13:37:02 +01:00
|
|
|
|
2011-06-03 00:53:23 +02:00
|
|
|
//Obtain the event object
|
|
|
|
if (!e)
|
|
|
|
{
|
|
|
|
e = window.event;
|
|
|
|
}
|
2011-02-26 20:21:55 +01:00
|
|
|
|
2011-06-03 00:53:23 +02:00
|
|
|
if (_egw_active_menu)
|
|
|
|
{
|
2014-10-24 13:15:33 +02:00
|
|
|
_egw_active_menu.hide();
|
2011-06-03 00:53:23 +02:00
|
|
|
}
|
2014-11-27 17:57:57 +01:00
|
|
|
else if (!e.ctrlKey && e.which == 3 || e.which === 0) // tap event indicates by 0
|
2011-06-03 00:53:23 +02:00
|
|
|
{
|
2014-10-24 13:15:33 +02:00
|
|
|
var _xy = ai._getPageXY(e);
|
2014-12-17 11:34:27 +01:00
|
|
|
var _implContext = {event:e, posx:_xy.posx, posy: _xy.posy};
|
|
|
|
_callback.call(_context, _implContext, ai);
|
2011-06-03 00:53:23 +02:00
|
|
|
}
|
2011-02-26 20:21:55 +01:00
|
|
|
|
2014-10-24 13:15:33 +02:00
|
|
|
e.cancelBubble = !e.ctrlKey || e.which == 1;
|
|
|
|
if (e.stopPropagation && e.cancelBubble)
|
2011-06-03 00:53:23 +02:00
|
|
|
{
|
|
|
|
e.stopPropagation();
|
2011-02-26 20:21:55 +01:00
|
|
|
}
|
2014-10-24 13:15:33 +02:00
|
|
|
return !e.cancelBubble;
|
|
|
|
};
|
2016-04-04 09:59:49 +02:00
|
|
|
// Safari still needs the taphold to trigger contextmenu
|
|
|
|
// Chrome has default event on touch and hold which acts like right click
|
2021-07-29 14:57:10 +02:00
|
|
|
this._handleTapHold(_node, contextHandler);
|
2022-05-25 17:40:42 +02:00
|
|
|
if (!egwIsMobile()) jQuery(_node).on('contextmenu', contextHandler);
|
2014-10-24 13:15:33 +02:00
|
|
|
};
|
2011-06-03 00:53:23 +02:00
|
|
|
|
|
|
|
ai.doRegisterAction = function(_aoi, _callback, _context)
|
|
|
|
{
|
|
|
|
var node = _aoi.getDOMNode();
|
2011-03-02 22:18:20 +01:00
|
|
|
|
2011-06-03 00:53:23 +02:00
|
|
|
if (node)
|
|
|
|
{
|
|
|
|
this._registerDefault(node, _callback, _context);
|
|
|
|
this._registerContext(node, _callback, _context);
|
2011-03-02 22:18:20 +01:00
|
|
|
return true;
|
2011-02-26 20:21:55 +01:00
|
|
|
}
|
2011-03-02 22:18:20 +01:00
|
|
|
return false;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
|
|
|
|
ai.doUnregisterAction = function(_aoi)
|
|
|
|
{
|
2020-08-08 18:51:39 +02:00
|
|
|
var node = _aoi.getDOMNode();
|
|
|
|
jQuery(node).off();
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
|
2011-02-26 21:40:27 +01:00
|
|
|
/**
|
|
|
|
* Builds the context menu and shows it at the given position/DOM-Node.
|
2014-10-24 14:44:15 +02:00
|
|
|
*
|
|
|
|
* @param {object} _context
|
|
|
|
* @param {type} _selected
|
|
|
|
* @param {type} _links
|
|
|
|
* @param {type} _target
|
|
|
|
* @returns {Boolean}
|
2011-02-26 21:40:27 +01:00
|
|
|
*/
|
2011-03-24 18:06:44 +01:00
|
|
|
ai.doExecuteImplementation = function(_context, _selected, _links, _target)
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
2011-03-24 18:06:44 +01:00
|
|
|
if (typeof _target == "undefined")
|
|
|
|
{
|
|
|
|
_target = null;
|
|
|
|
}
|
|
|
|
|
2014-11-10 18:50:58 +01:00
|
|
|
ai._context = _context;
|
2011-06-16 15:43:46 +02:00
|
|
|
if (typeof _context == "object" && typeof _context.keyEvent == "object")
|
|
|
|
{
|
|
|
|
return ai._handleKeyPress(_context.keyEvent, _selected, _links, _target);
|
|
|
|
}
|
|
|
|
else if (_context != "default")
|
2011-02-26 21:40:27 +01:00
|
|
|
{
|
2011-03-14 21:11:08 +01:00
|
|
|
//Check whether the context has the posx and posy parameters
|
|
|
|
if ((typeof _context.posx != "number" || typeof _context.posy != "number") &&
|
|
|
|
typeof _context.id != "undefined")
|
|
|
|
{
|
|
|
|
// Calculate context menu position from the given DOM-Node
|
|
|
|
var node = _context;
|
2011-02-26 21:40:27 +01:00
|
|
|
|
2016-06-02 16:51:15 +02:00
|
|
|
x = jQuery(node).offset().left;
|
|
|
|
y = jQuery(node).offset().top;
|
2011-02-26 21:40:27 +01:00
|
|
|
|
2014-10-24 14:44:15 +02:00
|
|
|
_context = {"posx": x, "posy": y};
|
2011-03-14 21:11:08 +01:00
|
|
|
}
|
|
|
|
|
2011-03-24 18:06:44 +01:00
|
|
|
var menu = ai._buildMenu(_links, _selected, _target);
|
2011-03-14 21:11:08 +01:00
|
|
|
menu.showAt(_context.posx, _context.posy);
|
|
|
|
|
|
|
|
return true;
|
2011-02-26 21:40:27 +01:00
|
|
|
}
|
2011-03-14 21:11:08 +01:00
|
|
|
else
|
|
|
|
{
|
2011-06-16 15:43:46 +02:00
|
|
|
var defaultAction = ai._getDefaultLink(_links);
|
2011-03-14 21:11:08 +01:00
|
|
|
if (defaultAction)
|
|
|
|
{
|
|
|
|
defaultAction.execute(_selected);
|
|
|
|
}
|
|
|
|
}
|
2011-02-26 21:40:27 +01:00
|
|
|
|
2011-03-14 21:11:08 +01:00
|
|
|
return false;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
|
2011-02-26 21:40:27 +01:00
|
|
|
/**
|
2011-03-23 15:05:39 +01:00
|
|
|
* Groups and sorts the given action tree layer
|
2014-10-24 14:44:15 +02:00
|
|
|
*
|
|
|
|
* @param {type} _layer
|
|
|
|
* @param {type} _links
|
|
|
|
* @param {type} _parentGroup
|
2011-02-26 21:40:27 +01:00
|
|
|
*/
|
2011-03-23 15:05:39 +01:00
|
|
|
ai._groupLayers = function(_layer, _links, _parentGroup)
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
2011-03-23 15:05:39 +01:00
|
|
|
// Seperate the multiple groups out of the layer
|
2011-02-26 20:21:55 +01:00
|
|
|
var link_groups = {};
|
2011-03-23 15:05:39 +01:00
|
|
|
|
|
|
|
for (var i = 0; i < _layer.children.length; i++)
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
2011-03-23 15:05:39 +01:00
|
|
|
var actionObj = _layer.children[i].action;
|
|
|
|
|
2011-02-26 20:21:55 +01:00
|
|
|
// Check whether the link group of the current element already exists,
|
|
|
|
// if not, create the group
|
2011-03-23 15:05:39 +01:00
|
|
|
var grp = actionObj.group;
|
2011-02-26 20:21:55 +01:00
|
|
|
if (typeof link_groups[grp] == "undefined")
|
|
|
|
{
|
|
|
|
link_groups[grp] = [];
|
|
|
|
}
|
|
|
|
|
2011-03-23 15:05:39 +01:00
|
|
|
// Search the link data for this action object if none is found,
|
|
|
|
// visible and enabled = true is assumed
|
|
|
|
var visible = true;
|
|
|
|
var enabled = true;
|
|
|
|
|
|
|
|
if (typeof _links[actionObj.id] != "undefined")
|
|
|
|
{
|
|
|
|
visible = _links[actionObj.id].visible;
|
|
|
|
enabled = _links[actionObj.id].enabled;
|
|
|
|
}
|
|
|
|
|
2011-02-26 20:21:55 +01:00
|
|
|
// Insert the element in order
|
|
|
|
var inserted = false;
|
2011-03-23 15:05:39 +01:00
|
|
|
var groupObj = {
|
|
|
|
"actionObj": actionObj,
|
|
|
|
"visible": visible,
|
|
|
|
"enabled": enabled,
|
|
|
|
"groups": []
|
|
|
|
};
|
|
|
|
|
|
|
|
for (var j = 0; j < link_groups[grp].length; j++)
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
2011-03-23 15:05:39 +01:00
|
|
|
var elem = link_groups[grp][j].actionObj;
|
|
|
|
if (elem.order > actionObj.order)
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
|
|
|
inserted = true;
|
2011-03-23 15:05:39 +01:00
|
|
|
link_groups[grp].splice(j, 0, groupObj);
|
2011-02-26 20:21:55 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the object hasn't been inserted, add it to the end of the list
|
|
|
|
if (!inserted)
|
|
|
|
{
|
2011-03-23 15:05:39 +01:00
|
|
|
link_groups[grp].push(groupObj);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this child itself has children, group those elements too
|
|
|
|
if (_layer.children[i].children.length > 0)
|
|
|
|
{
|
|
|
|
this._groupLayers(_layer.children[i], _links, groupObj);
|
2011-02-26 20:21:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-23 15:05:39 +01:00
|
|
|
// Transform the link_groups object into an sorted array
|
2011-02-26 20:21:55 +01:00
|
|
|
var groups = [];
|
2011-03-23 15:05:39 +01:00
|
|
|
|
2011-04-23 11:07:31 +02:00
|
|
|
for (var k in link_groups)
|
2011-03-23 15:05:39 +01:00
|
|
|
{
|
2011-02-26 20:21:55 +01:00
|
|
|
groups.push({"grp": k, "links": link_groups[k]});
|
2011-03-23 15:05:39 +01:00
|
|
|
}
|
|
|
|
|
2011-02-26 20:21:55 +01:00
|
|
|
groups.sort(function(a, b) {
|
2011-03-23 15:05:39 +01:00
|
|
|
var ia = parseInt(a.grp);
|
|
|
|
var ib = parseInt(b.grp);
|
|
|
|
return (ia > ib) ? 1 : ((ia < ib) ? -1 : 0);
|
2011-02-26 20:21:55 +01:00
|
|
|
});
|
|
|
|
|
2011-03-23 15:05:39 +01:00
|
|
|
// Append the groups to the groups2 array
|
|
|
|
var groups2 = [];
|
2011-03-25 14:12:24 +01:00
|
|
|
for (var i = 0; i < groups.length; i++)
|
2011-03-23 15:05:39 +01:00
|
|
|
{
|
2011-03-25 14:12:24 +01:00
|
|
|
groups2.push(groups[i].links);
|
2011-03-23 15:05:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_parentGroup.groups = groups2;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-03-23 15:05:39 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the menu layers
|
2014-10-24 14:44:15 +02:00
|
|
|
*
|
|
|
|
* @param {type} _menu
|
|
|
|
* @param {type} _groups
|
|
|
|
* @param {type} _selected
|
|
|
|
* @param {type} _enabled
|
|
|
|
* @param {type} _target
|
2011-03-23 15:05:39 +01:00
|
|
|
*/
|
2011-03-24 18:06:44 +01:00
|
|
|
ai._buildMenuLayer = function(_menu, _groups, _selected, _enabled, _target)
|
2011-03-23 15:05:39 +01:00
|
|
|
{
|
2011-03-23 21:08:33 +01:00
|
|
|
var firstGroup = true;
|
|
|
|
|
2011-03-23 15:05:39 +01:00
|
|
|
for (var i = 0; i < _groups.length; i++)
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
2011-03-23 21:08:33 +01:00
|
|
|
var firstElem = true;
|
2011-02-26 20:21:55 +01:00
|
|
|
|
|
|
|
// Go through the elements of each group
|
2011-03-23 15:05:39 +01:00
|
|
|
for (var j = 0; j < _groups[i].length; j++)
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
2011-03-23 15:05:39 +01:00
|
|
|
var link = _groups[i][j];
|
|
|
|
|
2011-02-26 20:21:55 +01:00
|
|
|
if (link.visible)
|
|
|
|
{
|
2011-03-23 21:08:33 +01:00
|
|
|
// Add an seperator after each group
|
|
|
|
if (!firstGroup && firstElem)
|
|
|
|
{
|
|
|
|
_menu.addItem("", "-");
|
|
|
|
}
|
|
|
|
firstElem = false;
|
|
|
|
|
2011-03-23 15:05:39 +01:00
|
|
|
var item = _menu.addItem(link.actionObj.id, link.actionObj.caption,
|
2011-02-26 20:21:55 +01:00
|
|
|
link.actionObj.iconUrl);
|
2011-03-14 21:11:08 +01:00
|
|
|
item["default"] = link.actionObj["default"];
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
|
|
|
|
// As this code is also used when a drag-drop popup menu is built,
|
|
|
|
// we have to perform this check
|
|
|
|
if (link.actionObj.type == "popup")
|
|
|
|
{
|
|
|
|
item.set_hint(link.actionObj.hint);
|
|
|
|
item.set_checkbox(link.actionObj.checkbox);
|
|
|
|
item.set_checked(link.actionObj.checked);
|
2016-04-28 18:39:53 +02:00
|
|
|
if(link.actionObj.checkbox && link.actionObj.isChecked)
|
|
|
|
{
|
|
|
|
item.set_checked(link.actionObj.isChecked.exec(link.actionObj, _selected));
|
|
|
|
}
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
item.set_groupIndex(link.actionObj.radioGroup);
|
2011-06-19 12:48:51 +02:00
|
|
|
|
2016-06-03 16:16:54 +02:00
|
|
|
if (link.actionObj.shortcut && !egwIsMobile())
|
2011-06-19 12:48:51 +02:00
|
|
|
{
|
|
|
|
var sc = link.actionObj.shortcut;
|
2011-06-25 16:27:30 +02:00
|
|
|
item.set_shortcutCaption(sc.caption);
|
2011-06-19 12:48:51 +02:00
|
|
|
}
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
item.set_data(link.actionObj);
|
2011-03-23 15:05:39 +01:00
|
|
|
if (link.enabled && _enabled)
|
2011-02-26 20:21:55 +01:00
|
|
|
{
|
|
|
|
item.set_onClick(function(elem) {
|
2014-11-10 18:50:58 +01:00
|
|
|
// Pass the context
|
|
|
|
elem.data.menu_context = ai._context;
|
|
|
|
|
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
|
|
|
// Copy the "checked" state
|
2011-04-17 19:55:37 +02:00
|
|
|
if (typeof elem.data.checked != "undefined")
|
|
|
|
{
|
|
|
|
elem.data.checked = elem.checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
elem.data.execute(_selected, _target);
|
|
|
|
|
|
|
|
if (typeof elem.data.checkbox != "undefined" && elem.data.checkbox)
|
|
|
|
{
|
|
|
|
return elem.data.checked;
|
|
|
|
}
|
2011-02-26 20:21:55 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item.set_enabled(false);
|
|
|
|
}
|
2011-03-23 15:05:39 +01:00
|
|
|
|
|
|
|
// Append the parent groups
|
|
|
|
if (link.groups)
|
|
|
|
{
|
2011-03-24 18:06:44 +01:00
|
|
|
this._buildMenuLayer(item, link.groups, _selected, link.enabled, _target);
|
2011-03-23 15:05:39 +01:00
|
|
|
}
|
2011-02-26 20:21:55 +01:00
|
|
|
}
|
|
|
|
}
|
2011-03-23 21:08:33 +01:00
|
|
|
|
|
|
|
firstGroup = firstGroup && firstElem;
|
2011-02-26 20:21:55 +01:00
|
|
|
}
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-03-23 15:05:39 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the context menu from the given action links
|
2014-10-24 14:44:15 +02:00
|
|
|
*
|
|
|
|
* @param {type} _links
|
|
|
|
* @param {type} _selected
|
|
|
|
* @param {type} _target
|
|
|
|
* @returns {egwMenu|egwActionImplementation._buildMenu.menu}
|
2011-03-23 15:05:39 +01:00
|
|
|
*/
|
2011-03-24 18:06:44 +01:00
|
|
|
ai._buildMenu = function(_links, _selected, _target)
|
2011-03-23 15:05:39 +01:00
|
|
|
{
|
|
|
|
// Build a tree containing all actions
|
|
|
|
var tree = {"root": []};
|
|
|
|
|
2014-10-30 23:18:37 +01:00
|
|
|
// Automatically add in Drag & Drop actions
|
2016-03-09 13:37:02 +01:00
|
|
|
if(this.auto_paste && !egwIsMobile())
|
2014-11-19 18:59:16 +01:00
|
|
|
{
|
|
|
|
this._addCopyPaste(_links,_selected);
|
|
|
|
}
|
2014-10-30 23:18:37 +01:00
|
|
|
|
2011-04-23 11:07:31 +02:00
|
|
|
for (var k in _links)
|
2011-03-23 15:05:39 +01:00
|
|
|
{
|
|
|
|
_links[k].actionObj.appendToTree(tree);
|
|
|
|
}
|
|
|
|
|
2014-10-24 14:44:15 +02:00
|
|
|
// We need the dummy object container in order to pass the array by
|
2011-03-23 15:36:25 +01:00
|
|
|
// reference
|
2011-03-23 15:05:39 +01:00
|
|
|
var groups = {
|
|
|
|
"groups": []
|
|
|
|
};
|
|
|
|
|
|
|
|
if (tree.root.length > 0)
|
|
|
|
{
|
|
|
|
// Sort every action object layer by the given sort position and grouping
|
|
|
|
this._groupLayers(tree.root[0], _links, groups);
|
|
|
|
}
|
|
|
|
|
|
|
|
var menu = new egwMenu();
|
|
|
|
|
|
|
|
// Build the menu layers
|
2011-03-24 18:06:44 +01:00
|
|
|
this._buildMenuLayer(menu, groups.groups, _selected, true, _target);
|
2011-02-26 20:21:55 +01:00
|
|
|
|
|
|
|
return menu;
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
|
2011-02-26 21:40:27 +01:00
|
|
|
ai._getPageXY = function getPageXY(event)
|
|
|
|
{
|
|
|
|
// document.body.scrollTop does not work in IE
|
|
|
|
var scrollTop = document.body.scrollTop ? document.body.scrollTop :
|
|
|
|
document.documentElement.scrollTop;
|
|
|
|
var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft :
|
|
|
|
document.documentElement.scrollLeft;
|
|
|
|
|
|
|
|
return {'posx': (event.clientX + scrollLeft), 'posy': (event.clientY + scrollTop)};
|
2014-10-24 14:44:15 +02:00
|
|
|
};
|
2011-02-26 21:40:27 +01:00
|
|
|
|
2014-10-30 23:18:37 +01:00
|
|
|
/**
|
|
|
|
* Automagically add in context menu items for copy and paste from
|
|
|
|
* drag and drop actions, based on current clipboard and the accepted types
|
|
|
|
*
|
|
|
|
* @param {object[]} _links Actions for inclusion in the menu
|
|
|
|
* @param {egwActionObject[]} _selected Currently selected entries
|
|
|
|
*/
|
|
|
|
ai._addCopyPaste = function (_links, _selected)
|
|
|
|
{
|
|
|
|
// Get a list of drag & drop actions
|
|
|
|
var drag = _selected[0].getSelectedLinks('drag').links;
|
|
|
|
var drop = _selected[0].getSelectedLinks('drop').links;
|
|
|
|
|
2020-06-03 16:57:51 +02:00
|
|
|
// No drags & no drops means early exit (only by default added egw_cancel_drop does NOT count!)
|
|
|
|
if ((!drag || jQuery.isEmptyObject(drag)) &&
|
|
|
|
(!drop || jQuery.isEmptyObject(drop) ||
|
|
|
|
Object.keys(drop).length === 1 && typeof drop.egw_cancel_drop !== 'undefined'))
|
2014-10-30 23:18:37 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find existing actions so we don't get copies
|
|
|
|
var mgr = _selected[0].manager;
|
|
|
|
var copy_action = mgr.getActionById('egw_copy');
|
2014-11-04 22:34:18 +01:00
|
|
|
var add_action = mgr.getActionById('egw_copy_add');
|
2014-12-16 17:27:33 +01:00
|
|
|
var clipboard_action = mgr.getActionById('egw_os_clipboard');
|
2014-10-30 23:18:37 +01:00
|
|
|
var paste_action = mgr.getActionById('egw_paste');
|
|
|
|
|
2014-11-06 22:33:23 +01:00
|
|
|
// Fake UI so we can simulate the position of the drop
|
2017-01-31 16:29:00 +01:00
|
|
|
var ui = {
|
|
|
|
position: {top: 0, left: 0},
|
|
|
|
offset: {top: 0, left: 0}
|
|
|
|
};
|
2014-12-17 11:34:27 +01:00
|
|
|
if(this._context.event)
|
2014-11-06 22:33:23 +01:00
|
|
|
{
|
2014-12-17 11:34:27 +01:00
|
|
|
var event = this._context.event.originalEvent;
|
|
|
|
ui.position = {top: event.pageY, left: event.pageX};
|
|
|
|
ui.offset = {top: event.offsetY, left: event.offsetX};
|
2014-11-06 22:33:23 +01:00
|
|
|
}
|
2014-10-30 23:18:37 +01:00
|
|
|
// Create default copy menu action
|
|
|
|
if(drag && !jQuery.isEmptyObject(drag))
|
|
|
|
{
|
|
|
|
// Don't re-add if it's there
|
|
|
|
if(copy_action == null)
|
|
|
|
{
|
|
|
|
// Create a drag action that allows linking
|
|
|
|
copy_action = mgr.addAction('popup', 'egw_copy', egw.lang('Copy to clipboard'), egw.image('copy'), function(action, selected) {
|
|
|
|
// Copied, now add to clipboard
|
|
|
|
var clipboard = {
|
|
|
|
type:[],
|
|
|
|
selected:[]
|
2014-10-31 09:31:23 +01:00
|
|
|
};
|
2014-10-30 23:18:37 +01:00
|
|
|
|
|
|
|
// When pasting we need to know the type of drag
|
|
|
|
for(var k in drag)
|
|
|
|
{
|
|
|
|
if(drag[k].enabled && drag[k].actionObj.dragType.length > 0)
|
|
|
|
{
|
|
|
|
clipboard.type = clipboard.type.concat(drag[k].actionObj.dragType);
|
|
|
|
}
|
|
|
|
}
|
2014-11-04 22:34:18 +01:00
|
|
|
clipboard.type = jQuery.unique(clipboard.type);
|
2014-10-30 23:18:37 +01:00
|
|
|
// egwAction is a circular structure and can't be stringified so just take what we want
|
|
|
|
// Hopefully that's enough for the action handlers
|
|
|
|
for(var k in selected)
|
|
|
|
{
|
2014-10-31 09:31:23 +01:00
|
|
|
if(selected[k].id) clipboard.selected.push({id:selected[k].id, data:selected[k].data});
|
2014-10-30 23:18:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Save it in session
|
|
|
|
egw.setSessionItem('phpgwapi', 'egw_clipboard', JSON.stringify(clipboard));
|
|
|
|
},true);
|
2014-11-18 00:57:15 +01:00
|
|
|
copy_action.group = 2.5;
|
2014-10-30 23:18:37 +01:00
|
|
|
}
|
2014-11-04 22:34:18 +01:00
|
|
|
if(add_action == null)
|
|
|
|
{
|
|
|
|
// Create an action to add selected to clipboard
|
|
|
|
add_action = mgr.addAction('popup', 'egw_copy_add', egw.lang('Add to clipboard'), egw.image('copy'), function(action, selected) {
|
|
|
|
// Copied, now add to clipboard
|
|
|
|
var clipboard = JSON.parse(egw.getSessionItem('phpgwapi', 'egw_clipboard')) || {
|
|
|
|
type:[],
|
|
|
|
selected:[]
|
|
|
|
};
|
|
|
|
|
|
|
|
// When pasting we need to know the type of drag
|
|
|
|
for(var k in drag)
|
|
|
|
{
|
|
|
|
if(drag[k].enabled && drag[k].actionObj.dragType.length > 0)
|
|
|
|
{
|
|
|
|
clipboard.type = clipboard.type.concat(drag[k].actionObj.dragType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
clipboard.type = jQuery.unique(clipboard.type);
|
|
|
|
// egwAction is a circular structure and can't be stringified so just take what we want
|
|
|
|
// Hopefully that's enough for the action handlers
|
|
|
|
for(var k in selected)
|
|
|
|
{
|
|
|
|
if(selected[k].id) clipboard.selected.push({id:selected[k].id, data:selected[k].data});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save it in session
|
|
|
|
egw.setSessionItem('phpgwapi', 'egw_clipboard', JSON.stringify(clipboard));
|
|
|
|
},true);
|
2014-11-18 00:57:15 +01:00
|
|
|
add_action.group = 2.5;
|
2014-11-04 22:34:18 +01:00
|
|
|
|
|
|
|
}
|
2014-12-16 17:27:33 +01:00
|
|
|
if(clipboard_action == null)
|
|
|
|
{
|
|
|
|
// Create an action to add selected to clipboard
|
2022-09-09 17:30:12 +02:00
|
|
|
clipboard_action = mgr.addAction('popup', 'egw_os_clipboard', egw.lang('Copy to OS clipboard'), egw.image('copy'), function (action)
|
|
|
|
{
|
2015-02-16 16:18:25 +01:00
|
|
|
|
2022-09-09 17:30:12 +02:00
|
|
|
if (document.queryCommandSupported('copy'))
|
2015-08-13 19:47:19 +02:00
|
|
|
{
|
2016-06-02 16:51:15 +02:00
|
|
|
jQuery(action.data.target).trigger('copy');
|
2015-08-13 19:47:19 +02:00
|
|
|
}
|
2022-09-09 17:30:12 +02:00
|
|
|
}, true);
|
2014-12-16 17:27:33 +01:00
|
|
|
clipboard_action.group = 2.5;
|
|
|
|
}
|
2021-05-12 21:13:49 +02:00
|
|
|
let os_clipboard_caption = "";
|
2022-09-09 17:30:12 +02:00
|
|
|
if (this._context.event)
|
2021-05-12 21:13:49 +02:00
|
|
|
{
|
|
|
|
os_clipboard_caption = this._context.event.originalEvent.target.innerText.trim();
|
|
|
|
clipboard_action.set_caption(egw.lang('Copy "%1"', os_clipboard_caption.length > 20 ? os_clipboard_caption.substring(0, 20) + '...' : os_clipboard_caption));
|
|
|
|
clipboard_action.data.target = this._context.event.originalEvent.target;
|
|
|
|
}
|
2022-09-09 17:30:12 +02:00
|
|
|
jQuery(clipboard_action.data.target).off('copy').on('copy', function (event)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
copyTextToClipboard(event, clipboard_action, os_clipboard_caption).then((successful) =>
|
2015-08-13 19:47:19 +02:00
|
|
|
{
|
2022-09-09 17:30:12 +02:00
|
|
|
if (successful)
|
|
|
|
{
|
|
|
|
// Clear message
|
|
|
|
egw.message('');
|
|
|
|
window.getSelection().removeAllRanges();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Show fail message
|
|
|
|
egw.message(egw.lang('Use Ctrl-C/Cmd-C to copy'));
|
|
|
|
}
|
|
|
|
});
|
2015-08-17 18:56:47 +02:00
|
|
|
|
2022-09-09 17:30:12 +02:00
|
|
|
}
|
|
|
|
catch (err)
|
2015-08-17 18:56:47 +02:00
|
|
|
{
|
|
|
|
}
|
2015-08-13 19:47:19 +02:00
|
|
|
});
|
2014-10-30 23:18:37 +01:00
|
|
|
if(typeof _links[copy_action.id] == 'undefined')
|
|
|
|
{
|
|
|
|
_links[copy_action.id] = {
|
|
|
|
"actionObj": copy_action,
|
|
|
|
"enabled": true,
|
|
|
|
"visible": true,
|
|
|
|
"cnt": 0
|
|
|
|
};
|
|
|
|
}
|
2014-11-04 22:34:18 +01:00
|
|
|
if(typeof _links[add_action.id] == 'undefined')
|
|
|
|
{
|
|
|
|
_links[add_action.id] = {
|
|
|
|
"actionObj": add_action,
|
|
|
|
"enabled": true,
|
|
|
|
"visible": true,
|
|
|
|
"cnt": 0
|
|
|
|
};
|
|
|
|
}
|
2014-12-16 17:27:33 +01:00
|
|
|
if(typeof _links[clipboard_action.id] == 'undefined')
|
|
|
|
{
|
|
|
|
_links[clipboard_action.id] = {
|
|
|
|
"actionObj": clipboard_action,
|
|
|
|
"enabled": os_clipboard_caption.length > 0,
|
|
|
|
"visible": os_clipboard_caption.length > 0,
|
|
|
|
"cnt": 0
|
|
|
|
};
|
|
|
|
}
|
2014-10-30 23:18:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create default paste menu item
|
|
|
|
if(drop && !jQuery.isEmptyObject(drop))
|
|
|
|
{
|
|
|
|
// Create paste action
|
|
|
|
// This injects the clipboard data and calls the original handler
|
|
|
|
var paste_exec = function(action, selected) {
|
|
|
|
// Add in clipboard as a sender
|
|
|
|
var clipboard = JSON.parse(egw.getSessionItem('phpgwapi', 'egw_clipboard'));
|
2014-11-06 22:33:23 +01:00
|
|
|
// Fake drop position
|
|
|
|
drop[action.id].actionObj.ui = ui;
|
2014-11-19 21:13:46 +01:00
|
|
|
// Set a flag so apps can tell the difference, if they need to
|
|
|
|
drop[action.id].actionObj.paste = true;
|
|
|
|
|
2014-10-30 23:18:37 +01:00
|
|
|
drop[action.id].actionObj.execute(clipboard.selected,selected[0]);
|
2014-11-19 21:13:46 +01:00
|
|
|
|
|
|
|
drop[action.id].actionObj.paste = false;
|
2014-10-30 23:18:37 +01:00
|
|
|
};
|
|
|
|
|
2014-10-31 09:31:23 +01:00
|
|
|
var clipboard = JSON.parse(egw.getSessionItem('phpgwapi', 'egw_clipboard')) || {
|
|
|
|
type:[],
|
|
|
|
selected:[]
|
|
|
|
};
|
2014-10-30 23:18:37 +01:00
|
|
|
|
|
|
|
// Don't re-add if action already exists
|
|
|
|
if(paste_action == null)
|
|
|
|
{
|
|
|
|
paste_action = mgr.addAction('popup', 'egw_paste', egw.lang('Paste'), egw.image('editpaste'), paste_exec,true);
|
2014-11-18 00:57:15 +01:00
|
|
|
paste_action.group = 2.5;
|
|
|
|
paste_action.order = 9;
|
2014-10-30 23:18:37 +01:00
|
|
|
paste_action.canHaveChildren.push('drop');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set hint to something resembling current clipboard
|
2021-03-17 17:29:43 +01:00
|
|
|
var hint = egw.lang('Clipboard') + ":\n";
|
2014-10-30 23:18:37 +01:00
|
|
|
paste_action.set_hint(hint);
|
|
|
|
// Add titles of entries
|
|
|
|
for(var i = 0; i < clipboard.selected.length; i++)
|
|
|
|
{
|
|
|
|
var id = clipboard.selected[i].id.split('::');
|
|
|
|
egw.link_title(id[0],id[1],function(title) {if(title)this.hint += title+"\n";},paste_action);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add into links so it's included in menu
|
2016-08-24 22:27:45 +02:00
|
|
|
if(paste_action && paste_action.enabled.exec(paste_action, clipboard.selected, _selected[0]))
|
2014-10-30 23:18:37 +01:00
|
|
|
{
|
2016-08-22 17:39:09 +02:00
|
|
|
if(typeof _links[paste_action.id] == 'undefined')
|
|
|
|
{
|
|
|
|
_links[paste_action.id] = {
|
|
|
|
"actionObj": paste_action,
|
|
|
|
"enabled": false,
|
|
|
|
"visible": clipboard != null,
|
|
|
|
"cnt": 0
|
|
|
|
};
|
|
|
|
}
|
|
|
|
while(paste_action.children.length > 0)
|
|
|
|
{
|
|
|
|
paste_action.children[0].remove();
|
|
|
|
}
|
2014-10-30 23:18:37 +01:00
|
|
|
|
2016-08-22 17:39:09 +02:00
|
|
|
// If nothing [valid] in the clipboard, don't bother with children
|
|
|
|
if(clipboard == null || typeof clipboard.type != 'object')
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2014-10-30 23:18:37 +01:00
|
|
|
|
2016-08-22 17:39:09 +02:00
|
|
|
// Add in actual actions as children
|
|
|
|
for(var k in drop)
|
2014-10-30 23:18:37 +01:00
|
|
|
{
|
2016-08-22 17:39:09 +02:00
|
|
|
// Add some choices - need to be a copy, or they interfere with
|
|
|
|
// the original
|
|
|
|
var drop_clone = jQuery.extend({},drop[k].actionObj);
|
2016-08-24 22:27:45 +02:00
|
|
|
var parent = paste_action.parent === drop_clone.parent ? paste_action : (paste_action.getActionById(drop_clone.parent.id) || paste_action);
|
|
|
|
drop_clone.parent = parent;
|
2016-08-22 17:39:09 +02:00
|
|
|
drop_clone.onExecute = new egwFnct(this, null, []);
|
2016-08-24 22:27:45 +02:00
|
|
|
drop_clone.children = [];
|
2016-08-22 17:39:09 +02:00
|
|
|
drop_clone.set_onExecute(paste_exec);
|
2016-08-24 22:27:45 +02:00
|
|
|
parent.children.push(drop_clone);
|
|
|
|
parent.allowOnMultiple = paste_action.allowOnMultiple && drop_clone.allowOnMultiple;
|
2016-08-22 17:39:09 +02:00
|
|
|
_links[k] = jQuery.extend({},drop[k]);
|
|
|
|
_links[k].actionObj = drop_clone;
|
|
|
|
|
|
|
|
// Drop is allowed if clipboard types intersect drop types
|
|
|
|
_links[k].enabled = false;
|
|
|
|
_links[k].visible = false;
|
|
|
|
for (var i = 0; i < drop_clone.acceptedTypes.length; i++)
|
2014-10-30 23:18:37 +01:00
|
|
|
{
|
2016-08-22 17:39:09 +02:00
|
|
|
if (clipboard.type.indexOf(drop_clone.acceptedTypes[i]) != -1)
|
|
|
|
{
|
|
|
|
_links[paste_action.id].enabled = true;
|
|
|
|
_links[k].enabled = true;
|
|
|
|
_links[k].visible = true;
|
|
|
|
break;
|
|
|
|
}
|
2014-10-30 23:18:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2011-02-26 20:21:55 +01:00
|
|
|
return ai;
|
|
|
|
}
|
|
|
|
|
2022-09-09 17:30:12 +02:00
|
|
|
/**
|
|
|
|
* Try some deprecated ways of copying to the OS clipboard
|
|
|
|
*
|
|
|
|
* @param event
|
|
|
|
* @param clipboard_action
|
|
|
|
* @param text
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
function fallbackCopyTextToClipboard(event, clipboard_action, text)
|
|
|
|
{
|
|
|
|
// Cancel any no-select css
|
|
|
|
var target = jQuery(clipboard_action.data.target);
|
|
|
|
var old_select = target.css('user-select');
|
|
|
|
target.css('user-select', 'all');
|
|
|
|
|
|
|
|
var range = document.createRange();
|
|
|
|
range.selectNode(clipboard_action.data.target);
|
|
|
|
window.getSelection().removeAllRanges();
|
|
|
|
window.getSelection().addRange(range);
|
|
|
|
|
|
|
|
target.css('user-select', old_select);
|
|
|
|
|
|
|
|
// detect we are in IE via checking setActive, since it's
|
|
|
|
// only supported in IE, and make sure there's clipboardData object
|
|
|
|
if (typeof event.target.setActive != 'undefined' && window.clipboardData)
|
|
|
|
{
|
|
|
|
window.clipboardData.setData('Text', jQuery(clipboard_action.data.target).text().trim());
|
|
|
|
}
|
|
|
|
if (event.clipboardData)
|
|
|
|
{
|
|
|
|
event.clipboardData.setData('text/plain', jQuery(clipboard_action.data.target).text().trim());
|
|
|
|
event.clipboardData.setData('text/html', jQuery(clipboard_action.data.target).html());
|
|
|
|
}
|
|
|
|
if (!window.clipboardData)
|
|
|
|
{
|
|
|
|
|
|
|
|
const textArea = document.createElement("textarea");
|
|
|
|
textArea.value = text;
|
|
|
|
|
|
|
|
// Avoid scrolling to bottom
|
|
|
|
textArea.style.top = "0";
|
|
|
|
textArea.style.left = "0";
|
|
|
|
textArea.style.position = "fixed";
|
|
|
|
|
|
|
|
document.body.appendChild(textArea);
|
|
|
|
textArea.focus();
|
|
|
|
textArea.select();
|
|
|
|
}
|
|
|
|
|
|
|
|
let successful = false;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
successful = document.execCommand('copy');
|
|
|
|
const msg = successful ? 'successful' : 'unsuccessful';
|
|
|
|
console.log('Fallback: Copying text command was ' + msg);
|
|
|
|
}
|
|
|
|
catch (err)
|
|
|
|
{
|
|
|
|
successful = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
document.body.removeChild(textArea);
|
|
|
|
return successful;
|
|
|
|
}
|
|
|
|
|
|
|
|
function copyTextToClipboard(event, action, text)
|
|
|
|
{
|
|
|
|
if (!navigator.clipboard)
|
|
|
|
{
|
|
|
|
let success = fallbackCopyTextToClipboard(event, action, text);
|
|
|
|
return Promise.resolve(success);
|
|
|
|
}
|
|
|
|
// Use Clipboard API
|
|
|
|
return navigator.clipboard.writeText(text);
|
|
|
|
}
|
2011-02-26 20:21:55 +01:00
|
|
|
|
|
|
|
|