<<~JS
function(input){
var files = input.files,
dt = new DataTransfer(),
opts = { cancelable: true, bubbles: true, dataTransfer: dt };
input.parentElement.removeChild(input);
if (dt.items){
for (var i=0; i<files.length; i++){
dt.items.add(files[i]);
}
} else {
Object.defineProperty(dt, "files", {
value: files,
writable: false
});
}
var dropEvent = new DragEvent('drop', opts);
this.dispatchEvent(dropEvent);
}
JS
<<~JS
let source = arguments[0];
const target = arguments[1];
const step_delay = arguments[2] * 1000;
const drop_modifiers = arguments[3];
const key_aliases = {
'cmd': 'meta',
'command': 'meta',
'control': 'ctrl',
};
function rectCenter(rect){
return new DOMPoint(
(rect.left + rect.right)/2,
(rect.top + rect.bottom)/2
);
}
function pointOnRect(pt, rect) {
var rectPt = rectCenter(rect);
var slope = (rectPt.y - pt.y) / (rectPt.x - pt.x);
if (pt.x <= rectPt.x) { // left side
var minXy = slope * (rect.left - pt.x) + pt.y;
if (rect.top <= minXy && minXy <= rect.bottom)
return new DOMPoint(rect.left, minXy);
}
if (pt.x >= rectPt.x) { // right side
var maxXy = slope * (rect.right - pt.x) + pt.y;
if (rect.top <= maxXy && maxXy <= rect.bottom)
return new DOMPoint(rect.right, maxXy);
}
if (pt.y <= rectPt.y) { // top side
var minYx = (rectPt.top - pt.y) / slope + pt.x;
if (rect.left <= minYx && minYx <= rect.right)
return new DOMPoint(minYx, rect.top);
}
if (pt.y >= rectPt.y) { // bottom side
var maxYx = (rect.bottom - pt.y) / slope + pt.x;
if (rect.left <= maxYx && maxYx <= rect.right)
return new DOMPoint(maxYx, rect.bottom);
}
return new DOMPoint(pt.x,pt.y);
}
function dragStart() {
return new Promise( resolve => {
var dragEvent = new DragEvent('dragstart', opts);
source.dispatchEvent(dragEvent);
setTimeout(resolve, step_delay)
})
}
function dragEnter() {
return new Promise( resolve => {
target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
let targetRect = target.getBoundingClientRect(),
sourceCenter = rectCenter(source.getBoundingClientRect());
drop_modifiers.map(key => key_aliases[key] || key)
.forEach(key => opts[key + 'Key'] = true);
// fire 2 dragover events to simulate dragging with a direction
let entryPoint = pointOnRect(sourceCenter, targetRect);
let dragOverOpts = Object.assign({clientX: entryPoint.x, clientY: entryPoint.y}, opts);
let dragOverEvent = new DragEvent('dragover', dragOverOpts);
target.dispatchEvent(dragOverEvent);
setTimeout(resolve, step_delay)
})
}
function dragOnto() {
return new Promise( resolve => {
var targetCenter = rectCenter(target.getBoundingClientRect());
dragOverOpts = Object.assign({clientX: targetCenter.x, clientY: targetCenter.y}, opts);
dragOverEvent = new DragEvent('dragover', dragOverOpts);
target.dispatchEvent(dragOverEvent);
setTimeout(resolve, step_delay, { drop: dragOverEvent.defaultPrevented, opts: dragOverOpts});
})
}
function dragLeave({ drop, opts: dragOverOpts }) {
return new Promise( resolve => {
var dragLeaveOptions = { ...opts, ...dragOverOpts };
var dragLeaveEvent = new DragEvent('dragleave', dragLeaveOptions);
target.dispatchEvent(dragLeaveEvent);
if (drop) {
var dropEvent = new DragEvent('drop', dragLeaveOptions);
target.dispatchEvent(dropEvent);
}
var dragEndEvent = new DragEvent('dragend', dragLeaveOptions);
source.dispatchEvent(dragEndEvent);
setTimeout(resolve, step_delay);
})
}
const dt = new DataTransfer();
const opts = { cancelable: true, bubbles: true, dataTransfer: dt };
while (source && !source.draggable) {
source = source.parentElement;
}
if (source.tagName == 'A'){
dt.setData('text/uri-list', source.href);
dt.setData('text', source.href);
}
if (source.tagName == 'IMG'){
dt.setData('text/uri-list', source.src);
dt.setData('text', source.src);
}
dragStart().then(dragEnter).then(dragOnto).then(dragLeave)
JS