mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-12-28 19:10:28 +00:00
Add mobiledragdrop shim plugin
This seems to work quite well for me - cc @xcazin
This commit is contained in:
parent
24f29ac605
commit
8744d77f88
@ -14,7 +14,8 @@
|
||||
"tiddlywiki/qrcode",
|
||||
"tiddlywiki/bibtex",
|
||||
"tiddlywiki/savetrail",
|
||||
"tiddlywiki/twitter"
|
||||
"tiddlywiki/twitter",
|
||||
"tiddlywiki/mobiledragdrop"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
|
@ -3,7 +3,8 @@
|
||||
"plugins": [
|
||||
"tiddlywiki/tiddlyweb",
|
||||
"tiddlywiki/filesystem",
|
||||
"tiddlywiki/highlight"
|
||||
"tiddlywiki/highlight",
|
||||
"tiddlywiki/mobiledragdrop"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
|
7
plugins/tiddlywiki/mobiledragdrop/files/LICENSE
Executable file
7
plugins/tiddlywiki/mobiledragdrop/files/LICENSE
Executable file
@ -0,0 +1,7 @@
|
||||
Copyright (c) 2013 Tim Ruffles
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
461
plugins/tiddlywiki/mobiledragdrop/files/ios-drag-drop.js
Executable file
461
plugins/tiddlywiki/mobiledragdrop/files/ios-drag-drop.js
Executable file
@ -0,0 +1,461 @@
|
||||
(function(doc) {
|
||||
|
||||
function _exposeIosHtml5DragDropShim(config) {
|
||||
log = noop; // noOp, remove this line to enable debugging
|
||||
|
||||
var coordinateSystemForElementFromPoint;
|
||||
|
||||
function main() {
|
||||
config = config || {};
|
||||
if (!config.hasOwnProperty("simulateAnchorClick")) config.simulateAnchorClick = true;
|
||||
|
||||
coordinateSystemForElementFromPoint = navigator.userAgent.match(/OS [1-4](?:_\d+)+ like Mac/) ? "page" : "client";
|
||||
|
||||
var div = doc.createElement('div');
|
||||
var dragDiv = 'draggable' in div;
|
||||
var evts = 'ondragstart' in div && 'ondrop' in div;
|
||||
|
||||
var needsPatch = !(dragDiv || evts) || /iPad|iPhone|iPod|Android/.test(navigator.userAgent);
|
||||
log((needsPatch ? "" : "not ") + "patching html5 drag drop");
|
||||
|
||||
if(!needsPatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!config.enableEnterLeave) {
|
||||
DragDrop.prototype.synthesizeEnterLeave = noop;
|
||||
}
|
||||
|
||||
if(config.holdToDrag){
|
||||
doc.addEventListener("touchstart", touchstartDelay(config.holdToDrag), {passive:false});
|
||||
}
|
||||
else {
|
||||
doc.addEventListener("touchstart", touchstart, {passive:false});
|
||||
}
|
||||
}
|
||||
|
||||
function DragDrop(event, el) {
|
||||
|
||||
this.dragData = {};
|
||||
this.dragDataTypes = [];
|
||||
this.dragImage = null;
|
||||
this.dragImageTransform = null;
|
||||
this.dragImageWebKitTransform = null;
|
||||
this.customDragImage = null;
|
||||
this.customDragImageX = null;
|
||||
this.customDragImageY = null;
|
||||
this.el = el || event.target;
|
||||
|
||||
log("dragstart");
|
||||
|
||||
if (this.dispatchDragStart()) {
|
||||
this.createDragImage();
|
||||
this.listen();
|
||||
}
|
||||
}
|
||||
|
||||
DragDrop.prototype = {
|
||||
listen: function() {
|
||||
var move = onEvt(doc, "touchmove", this.move, this);
|
||||
var end = onEvt(doc, "touchend", ontouchend, this);
|
||||
var cancel = onEvt(doc, "touchcancel", cleanup, this);
|
||||
|
||||
function ontouchend(event) {
|
||||
this.dragend(event, event.target);
|
||||
cleanup.call(this);
|
||||
}
|
||||
function cleanup() {
|
||||
log("cleanup");
|
||||
this.dragDataTypes = [];
|
||||
if (this.dragImage !== null) {
|
||||
this.dragImage.parentNode.removeChild(this.dragImage);
|
||||
this.dragImage = null;
|
||||
this.dragImageTransform = null;
|
||||
this.dragImageWebKitTransform = null;
|
||||
}
|
||||
this.customDragImage = null;
|
||||
this.customDragImageX = null;
|
||||
this.customDragImageY = null;
|
||||
this.el = this.dragData = null;
|
||||
return [move, end, cancel].forEach(function(handler) {
|
||||
return handler.off();
|
||||
});
|
||||
}
|
||||
},
|
||||
move: function(event) {
|
||||
event.preventDefault();
|
||||
var pageXs = [], pageYs = [];
|
||||
[].forEach.call(event.changedTouches, function(touch) {
|
||||
pageXs.push(touch.pageX);
|
||||
pageYs.push(touch.pageY);
|
||||
});
|
||||
|
||||
var x = average(pageXs) - (this.customDragImageX || parseInt(this.dragImage.offsetWidth, 10) / 2);
|
||||
var y = average(pageYs) - (this.customDragImageY || parseInt(this.dragImage.offsetHeight, 10) / 2);
|
||||
this.translateDragImage(x, y);
|
||||
|
||||
this.synthesizeEnterLeave(event);
|
||||
},
|
||||
// We use translate instead of top/left because of sub-pixel rendering and for the hope of better performance
|
||||
// http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
|
||||
translateDragImage: function(x, y) {
|
||||
var translate = "translate(" + x + "px," + y + "px) ";
|
||||
|
||||
if (this.dragImageWebKitTransform !== null) {
|
||||
this.dragImage.style["-webkit-transform"] = translate + this.dragImageWebKitTransform;
|
||||
}
|
||||
if (this.dragImageTransform !== null) {
|
||||
this.dragImage.style.transform = translate + this.dragImageTransform;
|
||||
}
|
||||
},
|
||||
synthesizeEnterLeave: function(event) {
|
||||
var target = elementFromTouchEvent(this.el,event)
|
||||
if (target != this.lastEnter) {
|
||||
if (this.lastEnter) {
|
||||
this.dispatchLeave(event);
|
||||
}
|
||||
this.lastEnter = target;
|
||||
if (this.lastEnter) {
|
||||
this.dispatchEnter(event);
|
||||
}
|
||||
}
|
||||
if (this.lastEnter) {
|
||||
this.dispatchOver(event);
|
||||
}
|
||||
},
|
||||
dragend: function(event) {
|
||||
|
||||
// we'll dispatch drop if there's a target, then dragEnd.
|
||||
// drop comes first http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
|
||||
log("dragend");
|
||||
|
||||
if (this.lastEnter) {
|
||||
this.dispatchLeave(event);
|
||||
}
|
||||
|
||||
var target = elementFromTouchEvent(this.el,event)
|
||||
if (target) {
|
||||
log("found drop target " + target.tagName);
|
||||
this.dispatchDrop(target, event);
|
||||
} else {
|
||||
log("no drop target");
|
||||
}
|
||||
|
||||
var dragendEvt = doc.createEvent("Event");
|
||||
dragendEvt.initEvent("dragend", true, true);
|
||||
this.el.dispatchEvent(dragendEvt);
|
||||
},
|
||||
dispatchDrop: function(target, event) {
|
||||
var dropEvt = doc.createEvent("Event");
|
||||
dropEvt.initEvent("drop", true, true);
|
||||
|
||||
var touch = event.changedTouches[0];
|
||||
var x = touch[coordinateSystemForElementFromPoint + 'X'];
|
||||
var y = touch[coordinateSystemForElementFromPoint + 'Y'];
|
||||
|
||||
var targetOffset = getOffset(target);
|
||||
|
||||
dropEvt.offsetX = x - targetOffset.x;
|
||||
dropEvt.offsetY = y - targetOffset.y;
|
||||
|
||||
dropEvt.dataTransfer = {
|
||||
types: this.dragDataTypes,
|
||||
getData: function(type) {
|
||||
return this.dragData[type];
|
||||
}.bind(this),
|
||||
dropEffect: "move"
|
||||
};
|
||||
dropEvt.preventDefault = function() {
|
||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=14638 - if we don't cancel it, we'll snap back
|
||||
}.bind(this);
|
||||
|
||||
once(doc, "drop", function() {
|
||||
log("drop event not canceled");
|
||||
},this);
|
||||
|
||||
target.dispatchEvent(dropEvt);
|
||||
},
|
||||
dispatchEnter: function(event) {
|
||||
|
||||
var enterEvt = doc.createEvent("Event");
|
||||
enterEvt.initEvent("dragenter", true, true);
|
||||
enterEvt.dataTransfer = {
|
||||
types: this.dragDataTypes,
|
||||
getData: function(type) {
|
||||
return this.dragData[type];
|
||||
}.bind(this)
|
||||
};
|
||||
|
||||
var touch = event.changedTouches[0];
|
||||
enterEvt.pageX = touch.pageX;
|
||||
enterEvt.pageY = touch.pageY;
|
||||
enterEvt.clientX = touch.clientX;
|
||||
enterEvt.clientY = touch.clientY;
|
||||
|
||||
this.lastEnter.dispatchEvent(enterEvt);
|
||||
},
|
||||
dispatchOver: function(event) {
|
||||
|
||||
var overEvt = doc.createEvent("Event");
|
||||
overEvt.initEvent("dragover", true, true);
|
||||
overEvt.dataTransfer = {
|
||||
types: this.dragDataTypes,
|
||||
getData: function(type) {
|
||||
return this.dragData[type];
|
||||
}.bind(this)
|
||||
};
|
||||
|
||||
var touch = event.changedTouches[0];
|
||||
overEvt.pageX = touch.pageX;
|
||||
overEvt.pageY = touch.pageY;
|
||||
overEvt.clientX = touch.clientX;
|
||||
overEvt.clientY = touch.clientY;
|
||||
|
||||
this.lastEnter.dispatchEvent(overEvt);
|
||||
},
|
||||
dispatchLeave: function(event) {
|
||||
|
||||
var leaveEvt = doc.createEvent("Event");
|
||||
leaveEvt.initEvent("dragleave", true, true);
|
||||
leaveEvt.dataTransfer = {
|
||||
types: this.dragDataTypes,
|
||||
getData: function(type) {
|
||||
return this.dragData[type];
|
||||
}.bind(this)
|
||||
};
|
||||
|
||||
var touch = event.changedTouches[0];
|
||||
leaveEvt.pageX = touch.pageX;
|
||||
leaveEvt.pageY = touch.pageY;
|
||||
leaveEvt.clientX = touch.clientX;
|
||||
leaveEvt.clientY = touch.clientY;
|
||||
|
||||
this.lastEnter.dispatchEvent(leaveEvt);
|
||||
this.lastEnter = null;
|
||||
},
|
||||
dispatchDragStart: function() {
|
||||
var evt = doc.createEvent("Event");
|
||||
evt.initEvent("dragstart", true, true);
|
||||
evt.dataTransfer = {
|
||||
setData: function(type, val) {
|
||||
this.dragData[type] = val;
|
||||
if (this.dragDataTypes.indexOf(type) == -1) {
|
||||
this.dragDataTypes[this.dragDataTypes.length] = type;
|
||||
}
|
||||
return val;
|
||||
}.bind(this),
|
||||
setDragImage: function(el, x, y){
|
||||
this.customDragImage = el;
|
||||
this.customDragImageX = x
|
||||
this.customDragImageY = y
|
||||
}.bind(this),
|
||||
dropEffect: "move"
|
||||
};
|
||||
return this.el.dispatchEvent(evt);
|
||||
},
|
||||
createDragImage: function() {
|
||||
if (this.customDragImage) {
|
||||
this.dragImage = this.customDragImage.cloneNode(true);
|
||||
duplicateStyle(this.customDragImage, this.dragImage);
|
||||
} else {
|
||||
this.dragImage = this.el.cloneNode(true);
|
||||
duplicateStyle(this.el, this.dragImage);
|
||||
}
|
||||
this.dragImage.style.opacity = "0.5";
|
||||
this.dragImage.style.position = "absolute";
|
||||
this.dragImage.style.left = "0px";
|
||||
this.dragImage.style.top = "0px";
|
||||
this.dragImage.style.zIndex = "999999";
|
||||
|
||||
var transform = this.dragImage.style.transform;
|
||||
if (typeof transform !== "undefined") {
|
||||
this.dragImageTransform = "";
|
||||
if (transform != "none") {
|
||||
this.dragImageTransform = transform.replace(/translate\(\D*\d+[^,]*,\D*\d+[^,]*\)\s*/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
var webkitTransform = this.dragImage.style["-webkit-transform"];
|
||||
if (typeof webkitTransform !== "undefined") {
|
||||
this.dragImageWebKitTransform = "";
|
||||
if (webkitTransform != "none") {
|
||||
this.dragImageWebKitTransform = webkitTransform.replace(/translate\(\D*\d+[^,]*,\D*\d+[^,]*\)\s*/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
this.translateDragImage(-9999, -9999);
|
||||
|
||||
doc.body.appendChild(this.dragImage);
|
||||
}
|
||||
};
|
||||
|
||||
// delayed touch start event
|
||||
function touchstartDelay(delay) {
|
||||
return function(evt){
|
||||
var el = evt.target;
|
||||
|
||||
do {
|
||||
if (elementIsDraggable(el)) {
|
||||
var heldItem = function() {
|
||||
end.off();
|
||||
cancel.off();
|
||||
scroll.off();
|
||||
touchstart(evt);
|
||||
};
|
||||
|
||||
var onReleasedItem = function() {
|
||||
end.off();
|
||||
cancel.off();
|
||||
scroll.off();
|
||||
clearTimeout(timer);
|
||||
};
|
||||
|
||||
var timer = setTimeout(heldItem, delay);
|
||||
|
||||
var end = onEvt(el, 'touchend', onReleasedItem, this);
|
||||
var cancel = onEvt(el, 'touchcancel', onReleasedItem, this);
|
||||
var scroll = onEvt(window, 'scroll', onReleasedItem, this);
|
||||
break;
|
||||
}
|
||||
} while ((el = el.parentNode) && el !== doc.body);
|
||||
};
|
||||
};
|
||||
|
||||
// event listeners
|
||||
function touchstart(evt) {
|
||||
var el = evt.target;
|
||||
do {
|
||||
if (elementIsDraggable(el)) {
|
||||
handleTouchStartOnAnchor(el);
|
||||
|
||||
evt.preventDefault();
|
||||
new DragDrop(evt,el);
|
||||
break;
|
||||
}
|
||||
} while((el = el.parentNode) && el !== doc.body);
|
||||
}
|
||||
|
||||
function elementIsDraggable(el){
|
||||
// if an element is not draggable either explicitly or implicitly we can exit immediately
|
||||
if(!el.draggable) return false;
|
||||
|
||||
// if an element has been explicitly set to be draggable we're good to go
|
||||
if(el.hasAttribute("draggable")) return true;
|
||||
|
||||
// otherwise we investigate the implicit option
|
||||
return (!config.requireExplicitDraggable);
|
||||
}
|
||||
|
||||
function elementIsAnchor(el){
|
||||
return el.tagName.toLowerCase() == "a";
|
||||
}
|
||||
|
||||
function handleTouchStartOnAnchor(el){
|
||||
// If draggable isn't explicitly set for anchors, then simulate a click event.
|
||||
// Otherwise plain old vanilla links will stop working.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Touch_events#Handling_clicks
|
||||
if (!el.hasAttribute("draggable") && elementIsAnchor(el) && config.simulateAnchorClick) {
|
||||
var clickEvt = document.createEvent("MouseEvents");
|
||||
clickEvt.initMouseEvent("click", true, true, el.ownerDocument.defaultView, 1,
|
||||
evt.screenX, evt.screenY, evt.clientX, evt.clientY,
|
||||
evt.ctrlKey, evt.altKey, evt.shiftKey, evt.metaKey, 0, null);
|
||||
el.dispatchEvent(clickEvt);
|
||||
log("Simulating click to anchor");
|
||||
}
|
||||
}
|
||||
|
||||
// DOM helpers
|
||||
function elementFromTouchEvent(el,event) {
|
||||
var touch = event.changedTouches[0];
|
||||
var target = doc.elementFromPoint(
|
||||
touch[coordinateSystemForElementFromPoint + "X"],
|
||||
touch[coordinateSystemForElementFromPoint + "Y"]
|
||||
);
|
||||
return target;
|
||||
}
|
||||
|
||||
//calculate the offset position of an element (relative to the window, not the document)
|
||||
function getOffset(el) {
|
||||
var rect = el.getBoundingClientRect();
|
||||
return {
|
||||
"x": rect.left,
|
||||
"y": rect.top
|
||||
};
|
||||
}
|
||||
|
||||
function onEvt(el, event, handler, context) {
|
||||
if(context) {
|
||||
handler = handler.bind(context);
|
||||
}
|
||||
el.addEventListener(event, handler, {passive:false});
|
||||
return {
|
||||
off: function() {
|
||||
return el.removeEventListener(event, handler, {passive:false});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function once(el, event, handler, context) {
|
||||
if(context) {
|
||||
handler = handler.bind(context);
|
||||
}
|
||||
function listener(evt) {
|
||||
handler(evt);
|
||||
return el.removeEventListener(event,listener);
|
||||
}
|
||||
return el.addEventListener(event,listener);
|
||||
}
|
||||
|
||||
// duplicateStyle expects dstNode to be a clone of srcNode
|
||||
function duplicateStyle(srcNode, dstNode) {
|
||||
// Is this node an element?
|
||||
if (srcNode.nodeType == 1) {
|
||||
// Remove any potential conflict attributes
|
||||
dstNode.removeAttribute("id");
|
||||
dstNode.removeAttribute("class");
|
||||
dstNode.removeAttribute("style");
|
||||
dstNode.removeAttribute("draggable");
|
||||
|
||||
// Clone the style
|
||||
var cs = window.getComputedStyle(srcNode);
|
||||
for (var i = 0; i < cs.length; i++) {
|
||||
var csName = cs[i];
|
||||
dstNode.style.setProperty(csName, cs.getPropertyValue(csName), cs.getPropertyPriority(csName));
|
||||
}
|
||||
|
||||
// Pointer events as none makes the drag image transparent to document.elementFromPoint()
|
||||
dstNode.style.pointerEvents = "none";
|
||||
}
|
||||
|
||||
// Do the same for the children
|
||||
if (srcNode.hasChildNodes()) {
|
||||
for (var j = 0; j < srcNode.childNodes.length; j++) {
|
||||
duplicateStyle(srcNode.childNodes[j], dstNode.childNodes[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// general helpers
|
||||
function log(msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
function average(arr) {
|
||||
if (arr.length === 0) return 0;
|
||||
return arr.reduce((function(s, v) {
|
||||
return v + s;
|
||||
}), 0) / arr.length;
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
main();
|
||||
|
||||
};
|
||||
|
||||
if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
module.exports = _exposeIosHtml5DragDropShim;
|
||||
} else if (typeof window !== 'undefined') {
|
||||
_exposeIosHtml5DragDropShim(window.iosDragDropShim);
|
||||
}
|
||||
})(document);
|
18
plugins/tiddlywiki/mobiledragdrop/files/tiddlywiki.files
Normal file
18
plugins/tiddlywiki/mobiledragdrop/files/tiddlywiki.files
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"tiddlers": [
|
||||
{
|
||||
"file": "ios-drag-drop.js",
|
||||
"fields": {
|
||||
"type": "application/javascript",
|
||||
"title": "$:/plugins/tiddlywiki/mobiledragdrop/ios-drag-drop.js"
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "LICENSE",
|
||||
"fields": {
|
||||
"type": "text/plain",
|
||||
"title": "$:/plugins/tiddlywiki/mobiledragdrop/license"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
7
plugins/tiddlywiki/mobiledragdrop/plugin.info
Normal file
7
plugins/tiddlywiki/mobiledragdrop/plugin.info
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"title": "$:/plugins/tiddlywiki/mobiledragdrop",
|
||||
"description": "Mobile drag and drop shim",
|
||||
"author": "Tim Ruffles, adapted by Jeremy Ruston ",
|
||||
"core-version": ">=5.0.0",
|
||||
"list": "readme license"
|
||||
}
|
7
plugins/tiddlywiki/mobiledragdrop/rawmarkup.tid
Normal file
7
plugins/tiddlywiki/mobiledragdrop/rawmarkup.tid
Normal file
@ -0,0 +1,7 @@
|
||||
title: $:/plugins/tiddlywiki/mobiledragdrop/rawmarkup
|
||||
tags: $:/tags/RawMarkupWikified
|
||||
|
||||
`<script>
|
||||
var iosDragDropShim = { enableEnterLeave: true, unholdToDrag: 300 };`
|
||||
{{$:/plugins/tiddlywiki/mobiledragdrop/ios-drag-drop.js}}
|
||||
`</script>`
|
5
plugins/tiddlywiki/mobiledragdrop/readme.tid
Normal file
5
plugins/tiddlywiki/mobiledragdrop/readme.tid
Normal file
@ -0,0 +1,5 @@
|
||||
title: $:/plugins/tiddlywiki/mobiledragdrop/readme
|
||||
|
||||
This plugin provides wraps a "shim" that enables HTML 5 compatible drag and drop operations on mobile browsers, including iOS and Android. The shim was created by Tim Ruffles and is published at https://github.com/timruffles/ios-html5-drag-drop-shim.
|
||||
|
||||
After installing the plugin it is necessary to save the HTML file a second time before it will be fully enabled.
|
Loading…
Reference in New Issue
Block a user