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
|
|
|
|
*/
|
|
|
|
|
2011-08-03 16:04:30 +02:00
|
|
|
/*egw:uses
|
|
|
|
egw_action;
|
|
|
|
egw_action_common;
|
|
|
|
egw_action_popup;
|
2016-06-06 17:38:20 +02:00
|
|
|
vendor.bower-asset.jquery.dist.jquery;
|
2011-08-03 16:04:30 +02:00
|
|
|
*/
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
import {egwAction,egwActionImplementation, egw_getObjectManager} from "./egw_action.js";
|
2021-07-06 12:41:16 +02:00
|
|
|
import {getPopupImplementation} from "./egw_action_popup.js";
|
2022-05-23 17:22:03 +02:00
|
|
|
import {EGW_AI_DRAG_OUT, EGW_AI_DRAG_OVER, EGW_AO_EXEC_THIS, EGW_AI_DRAG_ENTER} from "./egw_action_constants.js";
|
2021-06-08 14:11:59 +02:00
|
|
|
|
2011-03-24 18:06:44 +01:00
|
|
|
/**
|
|
|
|
* Register the drag and drop handlers
|
|
|
|
*/
|
|
|
|
if (typeof window._egwActionClasses == "undefined")
|
2014-02-25 16:02:49 +01:00
|
|
|
window._egwActionClasses = {};
|
2011-03-24 18:06:44 +01:00
|
|
|
_egwActionClasses["drag"] = {
|
|
|
|
"actionConstructor": egwDragAction,
|
|
|
|
"implementation": getDragImplementation
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
_egwActionClasses["drop"] = {
|
|
|
|
"actionConstructor": egwDropAction,
|
|
|
|
"implementation": getDropImplementation
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2014-02-25 16:02:49 +01:00
|
|
|
*
|
|
|
|
* @param {egwAction} _id
|
|
|
|
* @param {string} _handler
|
|
|
|
* @param {string} _caption
|
|
|
|
* @param {string} _icon
|
|
|
|
* @param {(string|function)} _onExecute
|
|
|
|
* @param {bool} _allowOnMultiple
|
|
|
|
* @returns {egwDragAction}
|
2011-03-24 18:06:44 +01:00
|
|
|
*/
|
2021-06-08 14:11:59 +02:00
|
|
|
export function egwDragAction(_id, _handler, _caption, _icon, _onExecute, _allowOnMultiple)
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
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;
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var
|
|
|
|
_dragActionImpl = null;
|
|
|
|
|
2021-06-08 14:11:59 +02:00
|
|
|
export function getDragImplementation()
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
if (!_dragActionImpl)
|
|
|
|
{
|
|
|
|
_dragActionImpl = new egwDragActionImplementation();
|
|
|
|
}
|
2014-02-25 16:02:49 +01:00
|
|
|
return _dragActionImpl;
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
|
2021-06-08 14:11:59 +02:00
|
|
|
export function egwDragActionImplementation()
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
var ai = new egwActionImplementation();
|
|
|
|
|
|
|
|
ai.type = "drag";
|
|
|
|
|
|
|
|
ai.helper = null;
|
|
|
|
ai.ddTypes = [];
|
|
|
|
ai.selected = [];
|
2014-10-24 14:19:16 +02:00
|
|
|
|
2014-10-17 18:34:14 +02:00
|
|
|
// Define default helper DOM
|
2014-10-20 13:41:59 +02:00
|
|
|
// default helper also can be called later in application code in order to customization
|
2014-10-17 18:34:14 +02:00
|
|
|
ai.defaultDDHelper = function (_selected)
|
|
|
|
{
|
|
|
|
// Table containing clone of rows
|
2016-06-02 16:51:15 +02:00
|
|
|
var table = jQuery(document.createElement("table")).addClass('egwGridView_grid et2_egw_action_ddHelper_row');
|
2014-10-17 18:34:14 +02:00
|
|
|
// tr element to use as last row to show lable more ...
|
2016-06-02 16:51:15 +02:00
|
|
|
var moreRow = jQuery(document.createElement('tr')).addClass('et2_egw_action_ddHelper_moreRow');
|
2014-10-17 18:34:14 +02:00
|
|
|
// Main div helper container
|
2016-06-02 16:51:15 +02:00
|
|
|
var div = jQuery(document.createElement("div")).append(table);
|
2014-10-24 14:19:16 +02:00
|
|
|
|
2014-10-17 18:34:14 +02:00
|
|
|
var rows = [];
|
|
|
|
// Maximum number of rows to show
|
|
|
|
var maxRows = 3;
|
2014-10-21 16:31:18 +02:00
|
|
|
// item label
|
|
|
|
var itemLabel = egw.lang(egw.link_get_registry(egw.app_name(),_selected.length > 1?'entries':'entry')||egw.app_name());
|
2014-10-24 14:19:16 +02:00
|
|
|
|
2014-10-17 18:34:14 +02:00
|
|
|
var index = 0;
|
2018-07-25 11:22:04 +02:00
|
|
|
|
|
|
|
// Take select all into account when counting number of rows, because they may not be
|
|
|
|
// in _selected object
|
2018-07-25 14:11:45 +02:00
|
|
|
var pseudoNumRows = (_selected[0] && _selected[0]._context && _selected[0]._context._selectionMgr &&
|
|
|
|
_selected[0]._context._selectionMgr._selectAll) ?
|
|
|
|
_selected[0]._context._selectionMgr._total : _selected.length;
|
2018-07-25 11:22:04 +02:00
|
|
|
|
2014-10-17 18:34:14 +02:00
|
|
|
for (var i = 0; i < _selected.length;i++)
|
|
|
|
{
|
2016-06-02 16:51:15 +02:00
|
|
|
var row = jQuery(_selected[i].iface.getDOMNode()).clone();
|
2014-10-17 18:34:14 +02:00
|
|
|
if (row)
|
|
|
|
{
|
|
|
|
rows.push(row);
|
|
|
|
table.append(row);
|
|
|
|
}
|
|
|
|
index++;
|
|
|
|
if (index == maxRows)
|
|
|
|
{
|
2014-10-20 13:41:59 +02:00
|
|
|
// Lable to show number of items
|
2016-06-02 16:51:15 +02:00
|
|
|
var spanCnt = jQuery(document.createElement('span'))
|
2014-10-20 13:41:59 +02:00
|
|
|
.addClass('et2_egw_action_ddHelper_itemsCnt')
|
|
|
|
.appendTo(div);
|
2014-10-24 14:19:16 +02:00
|
|
|
|
2018-07-25 11:22:04 +02:00
|
|
|
spanCnt.text(pseudoNumRows +' '+ itemLabel);
|
2014-10-21 16:31:18 +02:00
|
|
|
// Number of not shown rows
|
2018-07-25 11:22:04 +02:00
|
|
|
var restRows = pseudoNumRows - maxRows;
|
2014-10-20 13:41:59 +02:00
|
|
|
if (restRows)
|
|
|
|
{
|
2020-04-16 19:34:10 +02:00
|
|
|
moreRow.text(egw.lang("%1 more %2 selected ...", (pseudoNumRows - maxRows), itemLabel));
|
2014-10-20 13:41:59 +02:00
|
|
|
}
|
2014-10-17 18:34:14 +02:00
|
|
|
table.append(moreRow);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-10-24 14:19:16 +02:00
|
|
|
|
2016-06-02 16:51:15 +02:00
|
|
|
var text = jQuery(document.createElement('div')).addClass('et2_egw_action_ddHelper_tip');
|
2014-10-17 18:34:14 +02:00
|
|
|
div.append(text);
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2014-10-21 15:35:19 +02:00
|
|
|
// Add notice of Ctrl key, if supported
|
2014-10-17 18:34:14 +02:00
|
|
|
if('draggable' in document.createElement('span') &&
|
2014-10-21 16:31:18 +02:00
|
|
|
navigator && navigator.userAgent.indexOf('Chrome') >= 0 && egw.app_name() == 'filemanager') // currently only filemanager supports drag out
|
2014-10-17 18:34:14 +02:00
|
|
|
{
|
2023-05-22 16:46:43 +02:00
|
|
|
if (rows.length == 1)
|
|
|
|
{
|
|
|
|
text.text(egw.lang('You may darg file out to your desktop', itemLabel));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
text.text(egw.lang('Note: If you drag out these selected rows to desktop only the first selected row will be downloaded.', itemLabel));
|
|
|
|
}
|
2014-10-17 18:34:14 +02:00
|
|
|
}
|
2014-10-20 13:41:59 +02:00
|
|
|
// Final html DOM return as helper structor
|
2014-10-17 18:34:14 +02:00
|
|
|
return div;
|
2014-10-23 18:23:25 +02:00
|
|
|
};
|
2014-10-24 14:19:16 +02:00
|
|
|
|
2011-03-24 18:06:44 +01:00
|
|
|
ai.doRegisterAction = function(_aoi, _callback, _context)
|
|
|
|
{
|
2022-05-30 17:19:23 +02:00
|
|
|
var node = _aoi.getDOMNode() && _aoi.getDOMNode()[0] ? _aoi.getDOMNode()[0] : _aoi.getDOMNode();
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
if (node)
|
|
|
|
{
|
2011-03-25 14:12:24 +01:00
|
|
|
// Prevent selection
|
|
|
|
node.onselectstart = function () {
|
|
|
|
return false;
|
|
|
|
};
|
2023-02-27 19:16:43 +01:00
|
|
|
if (!(window.FileReader && 'draggable' in document.createElement('span')))
|
2013-08-29 00:31:14 +02:00
|
|
|
{
|
|
|
|
// No DnD support
|
|
|
|
return;
|
|
|
|
}
|
2014-02-25 16:02:49 +01:00
|
|
|
|
2013-08-29 00:31:14 +02:00
|
|
|
// It shouldn't be so hard to get the action...
|
|
|
|
var action = null;
|
|
|
|
var groups = _context.getActionImplementationGroups();
|
2023-02-27 19:16:43 +01:00
|
|
|
if (!groups.drag)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2023-05-17 14:10:34 +02:00
|
|
|
for (var i = 0; i < groups.drag.length; i++)
|
2013-08-29 00:31:14 +02:00
|
|
|
{
|
|
|
|
// dragType 'file' says it can be dragged as a file
|
2014-10-20 17:03:31 +02:00
|
|
|
if(groups.drag[i].link.actionObj.dragType == 'file' || groups.drag[i].link.actionObj.dragType.indexOf('file') > -1)
|
2013-08-29 00:31:14 +02:00
|
|
|
{
|
|
|
|
action = groups.drag[i].link.actionObj;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-10-24 13:15:33 +02:00
|
|
|
|
2023-05-17 14:10:34 +02:00
|
|
|
// Bind mouse handlers
|
|
|
|
jQuery(node).off("mousedown")
|
|
|
|
.on({
|
|
|
|
mousedown: function(event){
|
|
|
|
if (_context.isSelection(event)){
|
|
|
|
node.setAttribute("draggable", false);
|
|
|
|
}
|
|
|
|
else if(event.which != 3)
|
|
|
|
{
|
|
|
|
document.getSelection().removeAllRanges();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
mouseup: function (event){
|
|
|
|
if (_context.isSelection(event) && document.getSelection().type === 'Range'){
|
|
|
|
//let the draggable be reactivated by another click up as the range selection is
|
|
|
|
// not working as expected in shadow-dom as expected in all browsers
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
node.setAttribute("draggable", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set cursor back to auto. Seems FF can't handle cursor reversion
|
|
|
|
jQuery('body').css({cursor:'auto'});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2011-03-25 14:12:24 +01:00
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
node.setAttribute('draggable', true);
|
2022-05-20 16:02:20 +02:00
|
|
|
const dragstart = function(event) {
|
2023-05-17 14:10:34 +02:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
if (action && egw.app_name() == 'filemanager') {
|
2022-05-17 15:01:11 +02:00
|
|
|
if (_context.isSelection(event)) return;
|
|
|
|
|
|
|
|
// Get all selected
|
2023-05-17 14:10:34 +02:00
|
|
|
var selected = ai.selected;
|
2022-05-17 15:01:11 +02:00
|
|
|
|
|
|
|
// Set file data
|
2023-05-22 16:46:43 +02:00
|
|
|
for (let i = 0; i < 1; i++) {
|
2022-05-17 15:01:11 +02:00
|
|
|
let d = selected[i].data || egw.dataGetUIDdata(selected[i].id).data || {};
|
|
|
|
if (d && d.mime && d.download_url) {
|
|
|
|
var url = d.download_url;
|
|
|
|
|
|
|
|
// NEED an absolute URL
|
|
|
|
if (url[0] == '/') url = egw.link(url);
|
|
|
|
// egw.link adds the webserver, but that might not be an absolute URL - try again
|
|
|
|
if (url[0] == '/') url = window.location.origin + url;
|
2023-05-17 14:10:34 +02:00
|
|
|
event.dataTransfer.setData("DownloadURL", d.mime + ':' + d.name + ':' + url);
|
2014-10-16 16:41:30 +02:00
|
|
|
}
|
2022-05-17 15:01:11 +02:00
|
|
|
}
|
2022-05-20 16:02:20 +02:00
|
|
|
event.dataTransfer.effectAllowed = 'copy';
|
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
if (event.dataTransfer.types.length == 0) {
|
|
|
|
// No file data? Abort: drag does nothing
|
|
|
|
event.preventDefault();
|
|
|
|
return;
|
|
|
|
}
|
2022-05-20 16:02:20 +02:00
|
|
|
} else {
|
|
|
|
event.dataTransfer.effectAllowed = 'linkMove';
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
2023-05-17 14:10:34 +02:00
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
|
|
|
|
const data = {
|
|
|
|
ddTypes: ai.ddTypes,
|
|
|
|
selected: ai.selected.map((item) => {
|
|
|
|
return {id: item.id}
|
|
|
|
})
|
|
|
|
};
|
2014-02-25 16:02:49 +01:00
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
if (!ai.helper) {
|
2022-05-17 15:01:11 +02:00
|
|
|
ai.helper = ai.defaultDDHelper(ai.selected);
|
|
|
|
}
|
|
|
|
// Add a basic class to the helper in order to standardize the background layout
|
|
|
|
ai.helper[0].classList.add('et2_egw_action_ddHelper', 'ui-draggable-dragging');
|
|
|
|
document.body.append(ai.helper[0]);
|
|
|
|
this.classList.add('drag--moving');
|
2022-05-20 16:02:20 +02:00
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
event.dataTransfer.setData('application/json', JSON.stringify(data))
|
|
|
|
|
|
|
|
event.dataTransfer.setDragImage(ai.helper[0], 12, 12);
|
2022-05-20 16:02:20 +02:00
|
|
|
|
|
|
|
this.setAttribute('data-egwActionObjID', JSON.stringify(data.selected));
|
|
|
|
};
|
|
|
|
|
|
|
|
const dragend = function(event){
|
|
|
|
const helper = document.querySelector('.et2_egw_action_ddHelper');
|
|
|
|
if (helper) helper.remove();
|
2022-05-25 11:16:33 +02:00
|
|
|
const draggable = document.querySelector('.drag--moving');
|
|
|
|
if (draggable) draggable.classList.remove('drag--moving');
|
2023-05-22 11:45:17 +02:00
|
|
|
// cleanup drop hover class from all other DOMs if there's still anything left
|
|
|
|
Array.from(document.getElementsByClassName('et2dropzone drop-hover')).forEach(_i=>{_i.classList.remove('drop-hover')})
|
2022-05-20 16:02:20 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
// Drag Event listeners
|
|
|
|
node.addEventListener('dragstart', dragstart , false);
|
|
|
|
node.addEventListener('dragend', dragend, false);
|
|
|
|
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
ai.doUnregisterAction = function(_aoi)
|
|
|
|
{
|
2011-06-26 16:57:05 +02:00
|
|
|
var node = _aoi.getDOMNode();
|
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
if (node){
|
|
|
|
node.setAttribute('draggable', false);
|
2011-06-26 16:57:05 +02:00
|
|
|
}
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the context menu and shows it at the given position/DOM-Node.
|
2014-02-25 16:02:49 +01:00
|
|
|
*
|
|
|
|
* @param {string} _context
|
|
|
|
* @param {array} _selected
|
|
|
|
* @param {object} _links
|
2011-03-24 18:06:44 +01:00
|
|
|
*/
|
|
|
|
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.
|
2016-06-02 16:51:15 +02:00
|
|
|
var type = jQuery.isArray(_links[k].actionObj.dragType) ? _links[k].actionObj.dragType : [_links[k].actionObj.dragType];
|
2013-04-11 14:53:23 +02:00
|
|
|
for(var i = 0; i < type.length; i++)
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
2013-04-11 14:53:23 +02:00
|
|
|
if (this.ddTypes.indexOf(type[i]) == -1)
|
|
|
|
{
|
|
|
|
this.ddTypes.push(type[i]);
|
|
|
|
}
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no helper has been defined, create an default one
|
|
|
|
if (!this.helper && hasLink)
|
|
|
|
{
|
2014-10-17 18:34:14 +02:00
|
|
|
this.helper = ai.defaultDDHelper(_selected);
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
return ai;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The egwDropAction class overwrites the egwAction class and adds the "acceptedTypes"
|
2014-02-25 16:02:49 +01:00
|
|
|
* property. This array should contain all "dragTypes" the drop action is allowed to
|
|
|
|
*
|
|
|
|
* @param {egwAction} _id
|
|
|
|
* @param {string} _handler
|
|
|
|
* @param {string} _caption
|
|
|
|
* @param {string} _icon
|
|
|
|
* @param {(string|function)} _onExecute
|
|
|
|
* @param {bool} _allowOnMultiple
|
|
|
|
* @returns {egwDropAction}
|
2011-03-24 18:06:44 +01:00
|
|
|
*/
|
2021-06-08 14:11:59 +02:00
|
|
|
export function egwDropAction(_id, _handler, _caption, _icon, _onExecute, _allowOnMultiple)
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
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;
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
action.set_order = function(_value) {
|
|
|
|
action.order = _value;
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
action.set_group = function(_value) {
|
|
|
|
action.group = _value;
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The acceptType property allows strings as well as arrays - strings are
|
|
|
|
* automatically included in an array.
|
2014-02-25 16:02:49 +01:00
|
|
|
*
|
|
|
|
* @param {(string|array)} _value
|
2011-03-24 18:06:44 +01:00
|
|
|
*/
|
|
|
|
action.set_acceptedTypes = function(_value) {
|
|
|
|
if (_value instanceof Array)
|
|
|
|
{
|
|
|
|
action.acceptedTypes = _value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
action.acceptedTypes = [_value];
|
|
|
|
}
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
var
|
|
|
|
_dropActionImpl = null;
|
|
|
|
|
2021-06-08 14:11:59 +02:00
|
|
|
export function getDropImplementation()
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
if (!_dropActionImpl)
|
|
|
|
{
|
|
|
|
_dropActionImpl = new egwDropActionImplementation();
|
|
|
|
}
|
2014-02-25 16:02:49 +01:00
|
|
|
return _dropActionImpl;
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
|
|
|
|
2021-06-08 14:11:59 +02:00
|
|
|
export function egwDropActionImplementation()
|
2011-03-24 18:06:44 +01:00
|
|
|
{
|
|
|
|
var ai = new egwActionImplementation();
|
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
//keeps track of current drop element where dragged item's entered.
|
|
|
|
// it's necessary for dragenter/dragleave issue correction.
|
|
|
|
var currentDropEl = null;
|
|
|
|
|
2011-03-24 18:06:44 +01:00
|
|
|
ai.type = "drop";
|
|
|
|
|
|
|
|
ai.doRegisterAction = function(_aoi, _callback, _context)
|
|
|
|
{
|
2022-05-30 17:19:23 +02:00
|
|
|
var node = _aoi.getDOMNode() && _aoi.getDOMNode()[0] ? _aoi.getDOMNode()[0] : _aoi.getDOMNode();
|
2011-06-26 16:57:05 +02:00
|
|
|
var self = this;
|
2011-03-24 18:06:44 +01:00
|
|
|
if (node)
|
|
|
|
{
|
2022-05-17 15:01:11 +02:00
|
|
|
node.classList.add('et2dropzone');
|
2022-05-20 16:02:20 +02:00
|
|
|
const dragover = function (event) {
|
2022-05-17 15:01:11 +02:00
|
|
|
if (event.preventDefault) {
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
2022-05-20 16:02:20 +02:00
|
|
|
if (!self.getTheDraggedDOM()) return ;
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2022-09-15 12:40:49 +02:00
|
|
|
const data = {
|
2022-05-20 16:02:20 +02:00
|
|
|
event: event,
|
|
|
|
ui: self.getTheDraggedData()
|
|
|
|
};
|
|
|
|
_aoi.triggerEvent(EGW_AI_DRAG_OVER, data);
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
const dragenter = function (event) {
|
|
|
|
event.stopImmediatePropagation();
|
2022-05-17 15:01:11 +02:00
|
|
|
// don't trigger dragenter if we are entering the drag element
|
2022-05-20 16:02:20 +02:00
|
|
|
// don't go further if the dragged element is no there (happens when a none et2 dragged element is being dragged)
|
|
|
|
if (!self.getTheDraggedDOM() || self.isTheDraggedDOM(this) || this == currentDropEl) return;
|
|
|
|
|
|
|
|
currentDropEl = event.currentTarget;
|
|
|
|
event.dataTransfer.dropEffect = 'link';
|
2013-05-29 21:20:36 +02:00
|
|
|
|
2022-05-23 17:22:03 +02:00
|
|
|
const data = {
|
|
|
|
event: event,
|
|
|
|
ui: self.getTheDraggedData()
|
|
|
|
};
|
|
|
|
|
|
|
|
_aoi.triggerEvent(EGW_AI_DRAG_ENTER, data);
|
|
|
|
|
2022-09-15 12:40:49 +02:00
|
|
|
// cleanup drop hover class from all other DOMs if there's still anything left
|
|
|
|
Array.from(document.getElementsByClassName('et2dropzone drop-hover')).forEach(_i=>{_i.classList.remove('drop-hover')})
|
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
this.classList.add('drop-hover');
|
2013-05-29 21:20:36 +02:00
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
// stop the event from being fired for its children
|
2022-05-17 15:01:11 +02:00
|
|
|
event.preventDefault();
|
2022-05-20 16:02:20 +02:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
const drop = function (event) {
|
|
|
|
event.preventDefault();
|
|
|
|
// don't go further if the dragged element is no there (happens when a none et2 dragged element is being dragged)
|
|
|
|
if (!self.getTheDraggedDOM()) return ;
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
// remove the hover class
|
|
|
|
this.classList.remove('drop-hover');
|
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
const helper = self.getHelperDOM();
|
|
|
|
let ui = self.getTheDraggedData();
|
|
|
|
ui.position = {top: event.clientY, left: event.clientX};
|
|
|
|
ui.offset = {top: event.offsetY, left: event.offsetX};
|
2022-05-17 15:01:11 +02:00
|
|
|
|
|
|
|
|
|
|
|
let data = JSON.parse(event.dataTransfer.getData('application/json'));
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
if (!self.isAccepted(data, _context, _callback) || self.isTheDraggedDOM(this))
|
|
|
|
{
|
|
|
|
// clean up the helper dom
|
|
|
|
if (helper) helper.remove();
|
|
|
|
return;
|
|
|
|
}
|
2022-05-17 15:01:11 +02:00
|
|
|
|
|
|
|
let selected = data.selected.map((item) => {
|
|
|
|
return egw_getObjectManager(item.id, false)
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
var links = _callback.call(_context, "links", self, EGW_AO_EXEC_THIS);
|
|
|
|
|
|
|
|
// Disable all links which only accept types which are not
|
|
|
|
// inside ddTypes
|
|
|
|
for (var k in links) {
|
|
|
|
var accepted = links[k].actionObj.acceptedTypes;
|
|
|
|
|
|
|
|
var enabled = false;
|
|
|
|
for (var i = 0; i < data.ddTypes.length; i++) {
|
|
|
|
if (accepted.indexOf(data.ddTypes[i]) != -1) {
|
|
|
|
enabled = true;
|
|
|
|
break;
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
2022-05-17 15:01:11 +02:00
|
|
|
}
|
|
|
|
// Check for allowing multiple selected
|
|
|
|
if (!links[k].actionObj.allowOnMultiple && selected.length > 1) {
|
|
|
|
enabled = false;
|
|
|
|
}
|
|
|
|
if (!enabled) {
|
|
|
|
links[k].enabled = false;
|
|
|
|
links[k].visible = !links[k].actionObj.hideOnDisabled;
|
|
|
|
}
|
2011-03-24 18:06:44 +01:00
|
|
|
}
|
2022-05-17 15:01:11 +02:00
|
|
|
|
|
|
|
// Check whether there is only one link
|
|
|
|
var cnt = 0;
|
|
|
|
var lnk = null;
|
|
|
|
for (var k in links) {
|
|
|
|
if (links[k].enabled && links[k].visible) {
|
|
|
|
lnk = links[k];
|
|
|
|
cnt += 1 + links[k].actionObj.children.length;
|
|
|
|
|
|
|
|
// Add ui, so you know what happened where
|
|
|
|
lnk.actionObj.ui = ui;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cnt == 1) {
|
|
|
|
window.setTimeout(function () {
|
|
|
|
lnk.actionObj.execute(selected, _context);
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
// Don't add paste actions, this is a drop
|
|
|
|
popup.auto_paste = false;
|
|
|
|
|
|
|
|
window.setTimeout(function () {
|
|
|
|
popup.doExecuteImplementation(pos, selected, links,
|
|
|
|
_context);
|
|
|
|
// Reset, popup is reused
|
|
|
|
popup.auto_paste = true;
|
|
|
|
}, 0); // Timeout is needed to have it working in IE
|
|
|
|
}
|
|
|
|
// Set cursor back to auto. Seems FF can't handle cursor reversion
|
|
|
|
jQuery('body').css({cursor: 'auto'});
|
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
_aoi.triggerEvent(EGW_AI_DRAG_OUT, {event: event, ui: self.getTheDraggedData()});
|
2022-05-17 15:01:11 +02:00
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
// clean up the helper dom
|
|
|
|
if (helper) helper.remove();
|
2022-05-25 11:16:33 +02:00
|
|
|
self.getTheDraggedDOM().classList.remove('drag--moving');
|
2022-05-20 16:02:20 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const dragleave = function (event) {
|
|
|
|
event.stopImmediatePropagation();
|
2022-05-17 15:01:11 +02:00
|
|
|
|
|
|
|
// don't trigger dragleave if we are leaving the drag element
|
2022-05-20 16:02:20 +02:00
|
|
|
// don't go further if the dragged element is no there (happens when a none et2 dragged element is being dragged)
|
|
|
|
if (!self.getTheDraggedDOM() || self.isTheDraggedDOM(this) || this == currentDropEl) return;
|
2022-05-17 15:01:11 +02:00
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
const data = {
|
|
|
|
event: event,
|
|
|
|
ui: self.getTheDraggedData()
|
|
|
|
};
|
|
|
|
|
|
|
|
_aoi.triggerEvent(EGW_AI_DRAG_OUT, data);
|
2022-05-17 15:01:11 +02:00
|
|
|
|
|
|
|
this.classList.remove('drop-hover');
|
2022-05-20 16:02:20 +02:00
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// DND Event listeners
|
|
|
|
node.addEventListener('dragover', dragover, false);
|
|
|
|
|
|
|
|
node.addEventListener('dragenter', dragenter, false);
|
|
|
|
|
|
|
|
node.addEventListener('drop', drop, false);
|
|
|
|
|
|
|
|
node.addEventListener('dragleave', dragleave, false);
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
ai.isTheDraggedDOM = function (_dom)
|
|
|
|
{
|
|
|
|
return _dom.classList.contains('drag--moving');
|
|
|
|
}
|
|
|
|
|
2022-05-20 16:02:20 +02:00
|
|
|
ai.getTheDraggedDOM = function ()
|
|
|
|
{
|
|
|
|
return document.querySelector('.drag--moving');
|
|
|
|
}
|
|
|
|
|
|
|
|
ai.getHelperDOM = function ()
|
|
|
|
{
|
|
|
|
return document.querySelector('.et2_egw_action_ddHelper');
|
|
|
|
}
|
|
|
|
|
|
|
|
ai.getTheDraggedData = function()
|
|
|
|
{
|
|
|
|
let data = this.getTheDraggedDOM().dataset.egwactionobjid;
|
|
|
|
let selected = [];
|
|
|
|
if (data)
|
|
|
|
{
|
|
|
|
data = JSON.parse(data);
|
|
|
|
selected = data.map((item)=>{return egw_getObjectManager(item.id, false)});
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
draggable: this.getTheDraggedDOM(),
|
|
|
|
helper: this.getHelperDOM(),
|
|
|
|
selected: selected
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
// check if given draggable is accepted for drop
|
|
|
|
ai.isAccepted = function(_data, _context, _callback, _node)
|
|
|
|
{
|
|
|
|
if (_node && !_node.classList.contains('et2dropzone')) return false;
|
|
|
|
if (typeof _data.ddTypes != "undefined")
|
|
|
|
{
|
|
|
|
const accepted = this._fetchAccepted(
|
|
|
|
_callback.call(_context, "links", this, EGW_AO_EXEC_THIS));
|
|
|
|
|
|
|
|
// Check whether all drag types of the selected objects
|
|
|
|
// are accepted
|
|
|
|
var ddTypes = _data.ddTypes;
|
|
|
|
|
|
|
|
for (let i = 0; i < ddTypes.length; i++)
|
|
|
|
{
|
|
|
|
if (accepted.indexOf(ddTypes[i]) != -1)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2011-03-24 18:06:44 +01:00
|
|
|
ai.doUnregisterAction = function(_aoi)
|
|
|
|
{
|
2011-06-26 16:57:05 +02:00
|
|
|
var node = _aoi.getDOMNode();
|
|
|
|
|
2022-05-17 15:01:11 +02:00
|
|
|
if (node) {
|
|
|
|
node.classList.remove('et2dropzone');
|
2011-06-26 16:57:05 +02:00
|
|
|
}
|
2014-02-25 16:02:49 +01: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;
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the context menu and shows it at the given position/DOM-Node.
|
2014-02-25 16:02:49 +01:00
|
|
|
*
|
|
|
|
* @param {string} _context
|
|
|
|
* @param {array} _selected
|
|
|
|
* @param {object} _links
|
2011-03-24 18:06:44 +01:00
|
|
|
*/
|
|
|
|
ai.doExecuteImplementation = function(_context, _selected, _links)
|
|
|
|
{
|
|
|
|
if (_context == "links")
|
|
|
|
{
|
|
|
|
return _links;
|
|
|
|
}
|
2014-02-25 16:02:49 +01:00
|
|
|
};
|
2011-03-24 18:06:44 +01:00
|
|
|
|
|
|
|
return ai;
|
|
|
|
}
|