Activated a working HTML5 Drag & Drop polyfill for the Firefox Mobile browser

This commit is contained in:
djmaze 2020-09-28 20:04:09 +02:00
parent b8357fd739
commit 17575f19f0
3 changed files with 66 additions and 75 deletions

View file

@ -1,6 +1,5 @@
(() => { (doc => {
const doc = document, let visible = "visible",
visible = "visible",
wakeUp = () => { wakeUp = () => {
clearTimeout(timer); clearTimeout(timer);
if (status !== visible) { if (status !== visible) {
@ -12,24 +11,21 @@
dispatchEvent(new CustomEvent("idle")); dispatchEvent(new CustomEvent("idle"));
} }
}, 10000); }, 10000);
}; },
status = visible,
let status = visible, timer = 0,
timer = false,
init = () => { init = () => {
init = ()=>{}; init = ()=>{};
doc.addEventListener("visibilitychange", () => { doc.addEventListener("visibilitychange", () => {
status = doc.visibilityState; status = doc.visibilityState;
doc.hidden || wakeUp(); doc.hidden || wakeUp();
}, false); });
wakeUp(); wakeUp();
doc.addEventListener("mousemove", wakeUp); ["mousemove","keyup","touchstart"].forEach(t => doc.addEventListener(t, wakeUp));
doc.addEventListener("keyup", wakeUp);
doc.addEventListener("touchstart", wakeUp);
addEventListener("scroll", wakeUp); addEventListener("scroll", wakeUp);
}; };
window.ifvisible = { this.ifvisible = {
idle: callback => { idle: callback => {
init(); init();
addEventListener("idle", callback); addEventListener("idle", callback);
@ -39,4 +35,4 @@
return status === visible; return status === visible;
} }
}; };
})(); })(document);

View file

@ -8,9 +8,22 @@
(doc => { (doc => {
'use strict'; 'use strict';
let dropEffect = 'move', let ua = navigator.userAgent.toLowerCase(),
dropEffect = 'move',
effectAllowed = 'all', effectAllowed = 'all',
data = {}; data = {},
dataTransfer,
dragSource,
isDragging,
allowDrop,
lastTarget,
lastTouch,
pressHoldInterval,
img, imgCustom, imgOffset;
/* /*
class DataTransferItem class DataTransferItem
{ {
@ -52,42 +65,32 @@
} }
} }
let dataTransfer,
dragSource,
isDragging,
allowDrop,
lastTarget,
lastTouch,
pressHoldInterval;
const const
// copy styles/attributes from drag source to drag image element // copy styles/attributes from drag source to drag image element
rmvAtts = 'id,class,style,draggable'.split(','), rmvAtts = 'id,class,style,draggable'.split(','),
kbdProps = 'altKey,ctrlKey,metaKey,shiftKey'.split(','), kbdProps = 'altKey,ctrlKey,metaKey,shiftKey'.split(','),
ptProps = 'pageX,pageY,clientX,clientY,screenX,screenY,offsetX,offsetY'.split(','); ptProps = 'pageX,pageY,clientX,clientY,screenX,screenY,offsetX,offsetY'.split(','),
// clear all members // clear all members
function reset() { reset = () => {
destroyImage(); destroyImage();
dragSource = lastTouch = lastTarget = dataTransfer = null; dragSource = lastTouch = lastTarget = dataTransfer = null;
isDragging = allowDrop = false; isDragging = allowDrop = false;
clearInterval(pressHoldInterval); clearInterval(pressHoldInterval);
} },
// ignore events that have been handled or that involve more than one touch // ignore events that have been handled or that involve more than one touch
function shouldHandle(e) { shouldHandle = e => e && !e.defaultPrevented && e.touches && e.touches.length < 2,
return e && !e.defaultPrevented && e.touches && e.touches.length < 2;
}
// get point for a touch event // get point for a touch event
function getPoint(e) { getPoint = e => {
if (e && e.touches) { if (e && e.touches) {
e = e.touches[0]; e = e.touches[0];
} }
return { x: e.clientX, y: e.clientY }; return { x: e.clientX, y: e.clientY };
} },
function touchstart(e) { touchstart = e => {
if (shouldHandle(e)) { if (shouldHandle(e)) {
// clear all variables // clear all variables
reset(); reset();
@ -101,26 +104,26 @@
e.preventDefault(); // prevent scrolling NOTE: this creates a bug that click will not work e.preventDefault(); // prevent scrolling NOTE: this creates a bug that click will not work
// 1000 ms to wait, chrome on android triggers dragstart in 600 // 1000 ms to wait, chrome on android triggers dragstart in 600
pressHoldInterval = setTimeout(e => { pressHoldInterval = setTimeout(() => {
// start dragging // start dragging
if (dragSource && !isDragging) { if (dragSource && !isDragging) {
isDragging = true; isDragging = true;
let target = getTarget(e);
dataTransfer = new DataTransfer(); dataTransfer = new DataTransfer();
dispatchEvent(e, 'dragstart', dragSource); if ((isDragging = dispatchEvent(e, 'dragstart', dragSource))) {
createImage(e); createImage(e);
dispatchEvent(e, 'dragenter', target); dispatchEvent(e, 'dragenter', getTarget(e));
}
} }
}, 1000); }, 1000);
} }
} }
} },
function touchmove(e) { touchmove = e => {
if (isDragging) { if (isDragging) {
let target = getTarget(e);
// continue dragging // continue dragging
if (isDragging) { if (isDragging) {
let target = getTarget(e);
lastTouch = e; lastTouch = e;
e.preventDefault(); // prevent scrolling e.preventDefault(); // prevent scrolling
if (target != lastTarget) { if (target != lastTarget) {
@ -129,15 +132,14 @@
lastTarget = target; lastTarget = target;
} }
moveImage(e); moveImage(e);
allowDrop = dispatchEvent(e, 'dragover', target); allowDrop = !dispatchEvent(e, 'dragover', target);
} }
} else { } else {
reset(); reset();
return;
}
} }
},
function touchend(e) { touchend = e => {
if (shouldHandle(e)) { if (shouldHandle(e)) {
if (!isDragging) { if (!isDragging) {
// touched the element but didn't drag, so simulate a click // touched the element but didn't drag, so simulate a click
@ -152,21 +154,19 @@
reset(); reset();
} }
} }
} },
// get the element at a given touch event // get the element at a given touch event
function getTarget(e) { getTarget = e => {
let pt = getPoint(e), el = doc.elementFromPoint(pt.x, pt.y); let pt = getPoint(e), el = doc.elementFromPoint(pt.x, pt.y);
while (el && getComputedStyle(el).pointerEvents == 'none') { while (el && getComputedStyle(el).pointerEvents == 'none') {
el = el.parentElement; el = el.parentElement;
} }
return el; return el;
} },
let img, imgCustom, imgOffset;
// create drag image from source element // create drag image from source element
function createImage(e) { createImage = e => {
// just in case... // just in case...
destroyImage(); destroyImage();
// create drag image from custom element or drag source // create drag image from custom element or drag source
@ -185,17 +185,17 @@
} }
// add image to document // add image to document
moveImage(e); moveImage(e);
doc.body.appendChild(img); doc.body.append(img);
} },
// dispose of drag image element // dispose of drag image element
function destroyImage() { destroyImage = () => {
img && img.parentElement && img.remove(); img && img.remove();
img = imgCustom = null; img = imgCustom = null;
} },
// move the drag image element // move the drag image element
function moveImage(e) { moveImage = e => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (img) { if (img) {
let pt = getPoint(e), s = img.style; let pt = getPoint(e), s = img.style;
@ -203,17 +203,9 @@
s.top = Math.round(pt.y - imgOffset.y) + 'px'; s.top = Math.round(pt.y - imgOffset.y) + 'px';
} }
}); });
} },
// copy properties from an object to another copyStyle = (src, dst) => {
function copyProps(dst, src, props) {
for (let i = 0; i < props.length; i++) {
let p = props[i];
dst[p] = src[p];
}
}
function copyStyle(src, dst) {
// remove potentially troublesome attributes // remove potentially troublesome attributes
rmvAtts.forEach(function (att) { rmvAtts.forEach(function (att) {
dst.removeAttribute(att); dst.removeAttribute(att);
@ -238,25 +230,27 @@
for (i = 0; i < src.children.length; i++) { for (i = 0; i < src.children.length; i++) {
copyStyle(src.children[i], dst.children[i]); copyStyle(src.children[i], dst.children[i]);
} }
} },
function dispatchEvent(e, type, target) { // return false when cancelled
dispatchEvent = (e, type, target) => {
if (e && target) { if (e && target) {
let evt = new Event(type, {bubbles:true,cancelable:true}); let evt = new Event(type, {bubbles:true,cancelable:true});
evt.button = 0; evt.button = 0;
evt.which = evt.buttons = 1; evt.which = evt.buttons = 1;
copyProps(evt, e, kbdProps); // copy event properties into new event
copyProps(evt, e.touches ? e.touches[0] : e, ptProps); kbdProps.forEach(k => evt[k] = e[k]);
let src = e.touches ? e.touches[0] : e;
ptProps.forEach(k => evt[k] = src[k]);
if (isDragging) { if (isDragging) {
evt.dataTransfer = dataTransfer; evt.dataTransfer = dataTransfer;
} }
return !target.dispatchEvent(evt); return target.dispatchEvent(evt);
} }
return false; return false;
} };
// Chrome on mobile supports drag & drop // Chrome on mobile supports drag & drop
let ua = navigator.userAgent.toLowerCase();
if (ua.includes("mobile") && ua.includes('gecko/')) { if (ua.includes("mobile") && ua.includes('gecko/')) {
let opt = { passive: false, capture: false }; let opt = { passive: false, capture: false };
doc.addEventListener('touchstart', touchstart, opt); doc.addEventListener('touchstart', touchstart, opt);

View file

@ -74,16 +74,17 @@ config.paths.js = {
src: [ src: [
'dev/polyfill.js', 'dev/polyfill.js',
'dev/prototype.js', 'dev/prototype.js',
'dev/External/ifvisible.js',
'dev/dragdropgecko.js',
'dev/shortcuts.js',
'vendors/inputosaurus/inputosaurus.js', // custom (modified) 'vendors/inputosaurus/inputosaurus.js', // custom (modified)
'vendors/routes/signals.min.js', // fixed 'vendors/routes/signals.min.js', // fixed
'vendors/routes/hasher.min.js', // fixed 'vendors/routes/hasher.min.js', // fixed
'vendors/routes/crossroads.min.js', // fixed 'vendors/routes/crossroads.min.js', // fixed
'vendors/jua/jua.min.js', // custom 'vendors/jua/jua.min.js', // custom
'dev/shortcuts.js',
'vendors/qr.js/qr.min.js', // fixed (license) 'vendors/qr.js/qr.min.js', // fixed (license)
'vendors/bootstrap/js/bootstrap.native.min.js', // fixed 'vendors/bootstrap/js/bootstrap.native.min.js', // fixed
'vendors/knockout/build/output/knockout-latest.js', 'vendors/knockout/build/output/knockout-latest.js',
'dev/External/ifvisible.js',
'vendors/squire/build/squire-raw.js', 'vendors/squire/build/squire-raw.js',
'dev/External/SquireUI.js' 'dev/External/SquireUI.js'
] ]