Fixed taphandler problems.
This commit is contained in:
parent
e7c05a8387
commit
cd140a73f6
431
dist/iwmlib.js
vendored
431
dist/iwmlib.js
vendored
@ -3789,12 +3789,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEnd(event, interaction) {
|
onEnd(event, interaction) {
|
||||||
//console.log("Scatter.onEnd", this.dragging)
|
console.log("Scatter.onEnd", this.dragging);
|
||||||
if (interaction.isFinished()) {
|
if (interaction.isFinished()) {
|
||||||
this.endGesture(interaction);
|
this.endGesture(interaction);
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
for (let key of interaction.ended.keys()) {
|
for (let key of interaction.ended.keys()) {
|
||||||
if (interaction.isTap(key)) {
|
if (interaction.isTap(key)) {
|
||||||
|
console.log("Scatter.isTap");
|
||||||
let point = interaction.ended.get(key);
|
let point = interaction.ended.get(key);
|
||||||
this.onTap(event, interaction, point);
|
this.onTap(event, interaction, point);
|
||||||
}
|
}
|
||||||
@ -3819,7 +3820,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTap(event, interaction, point) {}
|
//onTap(event, interaction, point) {}
|
||||||
|
|
||||||
|
onTap(event, interaction, point) {
|
||||||
|
console.log("AbstractScatter.onTap", this.tapDelegate, interaction);
|
||||||
|
if (this.tapDelegate) {
|
||||||
|
Events.stop(event);
|
||||||
|
this.tapDelegate.tap(event, 'scatter');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onDragUpdate(delta) {
|
onDragUpdate(delta) {
|
||||||
if (this.onTransform != null) {
|
if (this.onTransform != null) {
|
||||||
@ -4366,13 +4376,6 @@
|
|||||||
TweenLite.set(this.element, { zIndex: DOMScatter$1.zIndex++ });
|
TweenLite.set(this.element, { zIndex: DOMScatter$1.zIndex++ });
|
||||||
}
|
}
|
||||||
|
|
||||||
onTap(event, interaction, point) {
|
|
||||||
if (this.tapDelegate) {
|
|
||||||
Events.stop(event);
|
|
||||||
this.tapDelegate.tap(event, 'scatter');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isDescendant(parent, child) {
|
isDescendant(parent, child) {
|
||||||
let node = child.parentNode;
|
let node = child.parentNode;
|
||||||
while (node != null) {
|
while (node != null) {
|
||||||
@ -4513,6 +4516,198 @@
|
|||||||
|
|
||||||
DOMScatter$1.zIndex = 1000;
|
DOMScatter$1.zIndex = 1000;
|
||||||
|
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
class CardWrapper extends Object {
|
||||||
|
constructor(domNode, { triggerSVGClicks = true, allowClickDistance = 44 } = {}) {
|
||||||
|
super();
|
||||||
|
this.domNode = domNode;
|
||||||
|
this.triggerSVGClicks = triggerSVGClicks;
|
||||||
|
this.allowClickDistance = allowClickDistance;
|
||||||
|
this.tapNodes = new Map();
|
||||||
|
this.tapHandler = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClicks() {
|
||||||
|
this.domNode.addEventListener(
|
||||||
|
'click',
|
||||||
|
event => {
|
||||||
|
if (event.isTrusted) {
|
||||||
|
Events.stop(event);
|
||||||
|
if (this.triggerSVGClicks && this.isSVGNode(event.target)) {
|
||||||
|
this.tap(event, 'triggerSVGClicks');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClicksAsTaps() {
|
||||||
|
this.domNode.addEventListener(
|
||||||
|
'click',
|
||||||
|
event => {
|
||||||
|
if (event.isTrusted) {
|
||||||
|
Events.stop(event);
|
||||||
|
}
|
||||||
|
this.tap(event);
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
isClickPrevented(node) {
|
||||||
|
if (node == null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (node.style && node.style.pointerEvents == 'none') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return this.isClickPrevented(node.parentNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
isClickable(node) {
|
||||||
|
if (node == null) return false
|
||||||
|
// console.log("isClickable", node, this.isClickPrevented(node))
|
||||||
|
if (this.isClickPrevented(node)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (node.tagName == 'A' && node.hasAttribute('href')) return true
|
||||||
|
if (node.hasAttribute('onclick')) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hasClickHandler(node) {
|
||||||
|
if (node == null) return false
|
||||||
|
if (this.tapNodes.has(node)) return true
|
||||||
|
for (let [selector, handler] of this.tapHandler.entries()) {
|
||||||
|
for (let obj of this.domNode.querySelectorAll(selector)) {
|
||||||
|
if (node == obj) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of all active nodes.
|
||||||
|
* Unfortunately we cannot search for all nodes with an attached 'click' event listener
|
||||||
|
* See https://stackoverflow.com/questions/11455515/how-to-check-whether-dynamically-attached-event-listener-exists-or-not
|
||||||
|
* Therefore we can only detect the following standard cases:
|
||||||
|
* I. All clickable objects like activeNodes
|
||||||
|
* II. Objects that have been attached a click handler by the scatter itself via
|
||||||
|
*/
|
||||||
|
activeNodes() {
|
||||||
|
let result = [];
|
||||||
|
for (let node of this.domNode.querySelectorAll('*')) {
|
||||||
|
if (this.isClickable(node)) result.push(node);
|
||||||
|
if (this.hasClickHandler(node)) result.push(node);
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
nearestActive(event) {
|
||||||
|
let element = this.domNode;
|
||||||
|
let activeNodes = this.activeNodes();
|
||||||
|
let globalClick = event.center ? event.center : { x: event.x, y: event.y };
|
||||||
|
let localClick = Points$1.fromPageToNode(element, globalClick);
|
||||||
|
|
||||||
|
let clickRects = activeNodes.map(link => {
|
||||||
|
let rect = link.getBoundingClientRect();
|
||||||
|
let topLeft = Points$1.fromPageToNode(element, rect);
|
||||||
|
let center = Points$1.fromPageToNode(element, {
|
||||||
|
x: rect.x + rect.width / 2,
|
||||||
|
y: rect.y + rect.height / 2
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
x: topLeft.x,
|
||||||
|
y: topLeft.y,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
center,
|
||||||
|
link
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let distances = [];
|
||||||
|
clickRects.forEach(rect => {
|
||||||
|
let distance = Points$1.distanceToRect(localClick, rect);
|
||||||
|
distances.push(parseInt(distance));
|
||||||
|
});
|
||||||
|
|
||||||
|
let closestClickIndex = distances.indexOf(Math.min(...distances));
|
||||||
|
let closestClickable = activeNodes[closestClickIndex];
|
||||||
|
if (distances[closestClickIndex] < this.allowClickDistance) {
|
||||||
|
return closestClickable
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
isSVGNode(node) {
|
||||||
|
return node.ownerSVGElement || node.tagName == 'svg'
|
||||||
|
}
|
||||||
|
|
||||||
|
simulateClick(node, event) {
|
||||||
|
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
|
||||||
|
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
|
||||||
|
Therefore we use the original click event (see constructor). */
|
||||||
|
if (this.isSVGNode(node)) {
|
||||||
|
if (this.triggerSVGClicks) {
|
||||||
|
let click = new Event('click');
|
||||||
|
node.dispatchEvent(click);
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
node.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeTapped(node, event) {
|
||||||
|
console.log('nodeTapped', node, this.isClickable(node));
|
||||||
|
if (this.isClickable(node)) {
|
||||||
|
this.simulateClick(node, event);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (this.tapNodes.has(node)) {
|
||||||
|
let handler = this.tapNodes.get(node);
|
||||||
|
handler(event, node);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (let [selector, handler] of this.tapHandler.entries()) {
|
||||||
|
console.log('nodeTapped', selector);
|
||||||
|
for (let obj of this.domNode.querySelectorAll(selector)) {
|
||||||
|
if (node == obj) {
|
||||||
|
handler(event, node);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
tap(event, calledBy = 'unknown') {
|
||||||
|
if (event.isTrusted) {
|
||||||
|
let node = this.nearestActive(event);
|
||||||
|
console.log('tap', node);
|
||||||
|
this.nodeTapped(node, event);
|
||||||
|
|
||||||
|
/* let node = document.elementFromPoint(event.clientX, event.clientY)
|
||||||
|
if (!this.nodeTapped(node, event)) {
|
||||||
|
node = this.nearestActive(event)
|
||||||
|
this.nodeTapped(node, event)
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTap(objOrSelector, handler) {
|
||||||
|
if (typeof objOrSelector == 'string') {
|
||||||
|
this.tapHandler.set(objOrSelector, handler);
|
||||||
|
} else {
|
||||||
|
this.tapNodes.set(objOrSelector, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
class CardLoader {
|
class CardLoader {
|
||||||
@ -4915,20 +5110,23 @@
|
|||||||
this.backBtn = element.querySelector('.backBtn');
|
this.backBtn = element.querySelector('.backBtn');
|
||||||
this.closeBtn = element.querySelector('.closeBtn');
|
this.closeBtn = element.querySelector('.closeBtn');
|
||||||
/* Buttons are not guaranteed to exist. */
|
/* Buttons are not guaranteed to exist. */
|
||||||
|
if (scatter.tapDelegate == null) {
|
||||||
|
let tapDelegate = new CardWrapper(element);
|
||||||
|
scatter.tapDelegate = tapDelegate;
|
||||||
|
}
|
||||||
if (this.infoBtn) {
|
if (this.infoBtn) {
|
||||||
scatter.addTapListener(this.infoBtn, event => {
|
scatter.tapDelegate.onTap(this.infoBtn, event => {
|
||||||
this.flip.start();
|
this.flip.start();
|
||||||
});
|
});
|
||||||
this.enable(this.infoBtn);
|
this.enable(this.infoBtn);
|
||||||
}
|
}
|
||||||
if (this.backBtn) {
|
if (this.backBtn) {
|
||||||
scatter.addTapListener(this.backBtn, event => {
|
scatter.tapDelegate.onTap(this.backBtn, event => {
|
||||||
this.start();
|
this.start();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.closeBtn) {
|
if (this.closeBtn) {
|
||||||
scatter.addTapListener(this.closeBtn, event => {
|
scatter.tapDelegate.onTap(this.closeBtn, event => {
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
this.enable(this.closeBtn);
|
this.enable(this.closeBtn);
|
||||||
@ -10088,198 +10286,6 @@
|
|||||||
zoomable: 0.5
|
zoomable: 0.5
|
||||||
};
|
};
|
||||||
|
|
||||||
/* eslint-disable no-console */
|
|
||||||
|
|
||||||
class CardWrapper extends Object {
|
|
||||||
constructor(domNode, { triggerSVGClicks = true, allowClickDistance = 44 } = {}) {
|
|
||||||
super();
|
|
||||||
this.domNode = domNode;
|
|
||||||
this.triggerSVGClicks = triggerSVGClicks;
|
|
||||||
this.allowClickDistance = allowClickDistance;
|
|
||||||
this.tapNodes = new Map();
|
|
||||||
this.tapHandler = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClicks() {
|
|
||||||
this.domNode.addEventListener(
|
|
||||||
'click',
|
|
||||||
event => {
|
|
||||||
if (event.isTrusted) {
|
|
||||||
Events.stop(event);
|
|
||||||
if (this.triggerSVGClicks && this.isSVGNode(event.target)) {
|
|
||||||
this.tap(event, 'triggerSVGClicks');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClicksAsTaps() {
|
|
||||||
this.domNode.addEventListener(
|
|
||||||
'click',
|
|
||||||
event => {
|
|
||||||
if (event.isTrusted) {
|
|
||||||
Events.stop(event);
|
|
||||||
}
|
|
||||||
this.tap(event);
|
|
||||||
},
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
isClickPrevented(node) {
|
|
||||||
if (node == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (node.style && node.style.pointerEvents == 'none') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return this.isClickPrevented(node.parentNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
isClickable(node) {
|
|
||||||
if (node == null) return false
|
|
||||||
// console.log("isClickable", node, this.isClickPrevented(node))
|
|
||||||
if (this.isClickPrevented(node)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (node.tagName == 'A' && node.hasAttribute('href')) return true
|
|
||||||
if (node.hasAttribute('onclick')) return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
hasClickHandler(node) {
|
|
||||||
if (node == null) return false
|
|
||||||
if (this.tapNodes.has(node)) return true
|
|
||||||
for (let [selector, handler] of this.tapHandler.entries()) {
|
|
||||||
for (let obj of this.domNode.querySelectorAll(selector)) {
|
|
||||||
if (node == obj) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of all active nodes.
|
|
||||||
* Unfortunately we cannot search for all nodes with an attached 'click' event listener
|
|
||||||
* See https://stackoverflow.com/questions/11455515/how-to-check-whether-dynamically-attached-event-listener-exists-or-not
|
|
||||||
* Therefore we can only detect the following standard cases:
|
|
||||||
* I. All clickable objects like activeNodes
|
|
||||||
* II. Objects that have been attached a click handler by the scatter itself via
|
|
||||||
*/
|
|
||||||
activeNodes() {
|
|
||||||
let result = [];
|
|
||||||
for (let node of this.domNode.querySelectorAll('*')) {
|
|
||||||
if (this.isClickable(node)) result.push(node);
|
|
||||||
if (this.hasClickHandler(node)) result.push(node);
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
nearestActive(event) {
|
|
||||||
let element = this.domNode;
|
|
||||||
let activeNodes = this.activeNodes();
|
|
||||||
let globalClick = event.center ? event.center : { x: event.x, y: event.y };
|
|
||||||
let localClick = Points$1.fromPageToNode(element, globalClick);
|
|
||||||
|
|
||||||
let clickRects = activeNodes.map(link => {
|
|
||||||
let rect = link.getBoundingClientRect();
|
|
||||||
let topLeft = Points$1.fromPageToNode(element, rect);
|
|
||||||
let center = Points$1.fromPageToNode(element, {
|
|
||||||
x: rect.x + rect.width / 2,
|
|
||||||
y: rect.y + rect.height / 2
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
x: topLeft.x,
|
|
||||||
y: topLeft.y,
|
|
||||||
width: rect.width,
|
|
||||||
height: rect.height,
|
|
||||||
center,
|
|
||||||
link
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let distances = [];
|
|
||||||
clickRects.forEach(rect => {
|
|
||||||
let distance = Points$1.distanceToRect(localClick, rect);
|
|
||||||
distances.push(parseInt(distance));
|
|
||||||
});
|
|
||||||
|
|
||||||
let closestClickIndex = distances.indexOf(Math.min(...distances));
|
|
||||||
let closestClickable = activeNodes[closestClickIndex];
|
|
||||||
if (distances[closestClickIndex] < this.allowClickDistance) {
|
|
||||||
return closestClickable
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
isSVGNode(node) {
|
|
||||||
return node.ownerSVGElement || node.tagName == 'svg'
|
|
||||||
}
|
|
||||||
|
|
||||||
simulateClick(node, event) {
|
|
||||||
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
|
|
||||||
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
|
|
||||||
Therefore we use the original click event (see constructor). */
|
|
||||||
if (this.isSVGNode(node)) {
|
|
||||||
if (this.triggerSVGClicks) {
|
|
||||||
let click = new Event('click');
|
|
||||||
node.dispatchEvent(click);
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
node.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeTapped(node, event) {
|
|
||||||
console.log('nodeTapped', node, this.isClickable(node));
|
|
||||||
if (this.isClickable(node)) {
|
|
||||||
this.simulateClick(node, event);
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (this.tapNodes.has(node)) {
|
|
||||||
handler = this.tapNodes.get(node);
|
|
||||||
handler(event, node);
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for (let [selector, handler] of this.tapHandler.entries()) {
|
|
||||||
console.log('nodeTapped', selector);
|
|
||||||
for (let obj of this.domNode.querySelectorAll(selector)) {
|
|
||||||
if (node == obj) {
|
|
||||||
handler(event, node);
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
tap(event, calledBy = 'unknown') {
|
|
||||||
if (event.isTrusted) {
|
|
||||||
let node = this.nearestActive(event);
|
|
||||||
console.log('tap', node);
|
|
||||||
this.nodeTapped(node, event);
|
|
||||||
|
|
||||||
/* let node = document.elementFromPoint(event.clientX, event.clientY)
|
|
||||||
if (!this.nodeTapped(node, event)) {
|
|
||||||
node = this.nearestActive(event)
|
|
||||||
this.nodeTapped(node, event)
|
|
||||||
} */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onTap(objOrSelector, handler) {
|
|
||||||
if (typeof objOrSelector == 'string') {
|
|
||||||
this.tapHandler.set(objOrSelector, handler);
|
|
||||||
} else {
|
|
||||||
this.tapNodes.set(objOrSelector, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends the card with scatter functionality.
|
* Extends the card with scatter functionality.
|
||||||
*
|
*
|
||||||
@ -10972,6 +10978,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
speak() {
|
speak() {
|
||||||
|
/**
|
||||||
|
* This is a little bit ugly, but imho the most elegant of all dirty solutions.
|
||||||
|
*
|
||||||
|
5ht * Within the plugins we have no knowledge of other cards and such. But must differentiate the
|
||||||
|
* clicks by their corresponding owner. The SpeechUtterance just takes a text and has no knowledge
|
||||||
|
* about the node that is currently read to the user.
|
||||||
|
*
|
||||||
|
* This means, that we can identify same text, but not differentiate same text on different nodes.
|
||||||
|
* To account for that, we add the node to the speechSynthesis object (#benefitsOfJavaScript) and
|
||||||
|
* have access to the node, by - let's say - expanding the functionality of the SpeechSynthesis object.
|
||||||
|
*
|
||||||
|
* SO -17.07.19
|
||||||
|
*/
|
||||||
|
|
||||||
|
let activeNode = window.speechSynthesis['speechPluginNode'];
|
||||||
this._updateText();
|
this._updateText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
224
dist/iwmlib.pixi.js
vendored
224
dist/iwmlib.pixi.js
vendored
@ -7409,12 +7409,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEnd(event, interaction) {
|
onEnd(event, interaction) {
|
||||||
//console.log("Scatter.onEnd", this.dragging)
|
console.log("Scatter.onEnd", this.dragging);
|
||||||
if (interaction.isFinished()) {
|
if (interaction.isFinished()) {
|
||||||
this.endGesture(interaction);
|
this.endGesture(interaction);
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
for (let key of interaction.ended.keys()) {
|
for (let key of interaction.ended.keys()) {
|
||||||
if (interaction.isTap(key)) {
|
if (interaction.isTap(key)) {
|
||||||
|
console.log("Scatter.isTap");
|
||||||
let point = interaction.ended.get(key);
|
let point = interaction.ended.get(key);
|
||||||
this.onTap(event, interaction, point);
|
this.onTap(event, interaction, point);
|
||||||
}
|
}
|
||||||
@ -7439,7 +7440,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTap(event, interaction, point) {}
|
//onTap(event, interaction, point) {}
|
||||||
|
|
||||||
|
onTap(event, interaction, point) {
|
||||||
|
console.log("AbstractScatter.onTap", this.tapDelegate, interaction);
|
||||||
|
if (this.tapDelegate) {
|
||||||
|
Events$1.stop(event);
|
||||||
|
this.tapDelegate.tap(event, 'scatter');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onDragUpdate(delta) {
|
onDragUpdate(delta) {
|
||||||
if (this.onTransform != null) {
|
if (this.onTransform != null) {
|
||||||
@ -7806,13 +7816,6 @@
|
|||||||
TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ });
|
TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ });
|
||||||
}
|
}
|
||||||
|
|
||||||
onTap(event, interaction, point) {
|
|
||||||
if (this.tapDelegate) {
|
|
||||||
Events$1.stop(event);
|
|
||||||
this.tapDelegate.tap(event, 'scatter');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isDescendant(parent, child) {
|
isDescendant(parent, child) {
|
||||||
let node = child.parentNode;
|
let node = child.parentNode;
|
||||||
while (node != null) {
|
while (node != null) {
|
||||||
@ -7953,6 +7956,198 @@
|
|||||||
|
|
||||||
DOMScatter.zIndex = 1000;
|
DOMScatter.zIndex = 1000;
|
||||||
|
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
class CardWrapper extends Object {
|
||||||
|
constructor(domNode, { triggerSVGClicks = true, allowClickDistance = 44 } = {}) {
|
||||||
|
super();
|
||||||
|
this.domNode = domNode;
|
||||||
|
this.triggerSVGClicks = triggerSVGClicks;
|
||||||
|
this.allowClickDistance = allowClickDistance;
|
||||||
|
this.tapNodes = new Map();
|
||||||
|
this.tapHandler = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClicks() {
|
||||||
|
this.domNode.addEventListener(
|
||||||
|
'click',
|
||||||
|
event => {
|
||||||
|
if (event.isTrusted) {
|
||||||
|
Events$1.stop(event);
|
||||||
|
if (this.triggerSVGClicks && this.isSVGNode(event.target)) {
|
||||||
|
this.tap(event, 'triggerSVGClicks');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClicksAsTaps() {
|
||||||
|
this.domNode.addEventListener(
|
||||||
|
'click',
|
||||||
|
event => {
|
||||||
|
if (event.isTrusted) {
|
||||||
|
Events$1.stop(event);
|
||||||
|
}
|
||||||
|
this.tap(event);
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
isClickPrevented(node) {
|
||||||
|
if (node == null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (node.style && node.style.pointerEvents == 'none') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return this.isClickPrevented(node.parentNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
isClickable(node) {
|
||||||
|
if (node == null) return false
|
||||||
|
// console.log("isClickable", node, this.isClickPrevented(node))
|
||||||
|
if (this.isClickPrevented(node)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (node.tagName == 'A' && node.hasAttribute('href')) return true
|
||||||
|
if (node.hasAttribute('onclick')) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hasClickHandler(node) {
|
||||||
|
if (node == null) return false
|
||||||
|
if (this.tapNodes.has(node)) return true
|
||||||
|
for (let [selector, handler] of this.tapHandler.entries()) {
|
||||||
|
for (let obj of this.domNode.querySelectorAll(selector)) {
|
||||||
|
if (node == obj) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of all active nodes.
|
||||||
|
* Unfortunately we cannot search for all nodes with an attached 'click' event listener
|
||||||
|
* See https://stackoverflow.com/questions/11455515/how-to-check-whether-dynamically-attached-event-listener-exists-or-not
|
||||||
|
* Therefore we can only detect the following standard cases:
|
||||||
|
* I. All clickable objects like activeNodes
|
||||||
|
* II. Objects that have been attached a click handler by the scatter itself via
|
||||||
|
*/
|
||||||
|
activeNodes() {
|
||||||
|
let result = [];
|
||||||
|
for (let node of this.domNode.querySelectorAll('*')) {
|
||||||
|
if (this.isClickable(node)) result.push(node);
|
||||||
|
if (this.hasClickHandler(node)) result.push(node);
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
nearestActive(event) {
|
||||||
|
let element = this.domNode;
|
||||||
|
let activeNodes = this.activeNodes();
|
||||||
|
let globalClick = event.center ? event.center : { x: event.x, y: event.y };
|
||||||
|
let localClick = Points.fromPageToNode(element, globalClick);
|
||||||
|
|
||||||
|
let clickRects = activeNodes.map(link => {
|
||||||
|
let rect = link.getBoundingClientRect();
|
||||||
|
let topLeft = Points.fromPageToNode(element, rect);
|
||||||
|
let center = Points.fromPageToNode(element, {
|
||||||
|
x: rect.x + rect.width / 2,
|
||||||
|
y: rect.y + rect.height / 2
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
x: topLeft.x,
|
||||||
|
y: topLeft.y,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
center,
|
||||||
|
link
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let distances = [];
|
||||||
|
clickRects.forEach(rect => {
|
||||||
|
let distance = Points.distanceToRect(localClick, rect);
|
||||||
|
distances.push(parseInt(distance));
|
||||||
|
});
|
||||||
|
|
||||||
|
let closestClickIndex = distances.indexOf(Math.min(...distances));
|
||||||
|
let closestClickable = activeNodes[closestClickIndex];
|
||||||
|
if (distances[closestClickIndex] < this.allowClickDistance) {
|
||||||
|
return closestClickable
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
isSVGNode(node) {
|
||||||
|
return node.ownerSVGElement || node.tagName == 'svg'
|
||||||
|
}
|
||||||
|
|
||||||
|
simulateClick(node, event) {
|
||||||
|
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
|
||||||
|
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
|
||||||
|
Therefore we use the original click event (see constructor). */
|
||||||
|
if (this.isSVGNode(node)) {
|
||||||
|
if (this.triggerSVGClicks) {
|
||||||
|
let click = new Event('click');
|
||||||
|
node.dispatchEvent(click);
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
node.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeTapped(node, event) {
|
||||||
|
console.log('nodeTapped', node, this.isClickable(node));
|
||||||
|
if (this.isClickable(node)) {
|
||||||
|
this.simulateClick(node, event);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (this.tapNodes.has(node)) {
|
||||||
|
let handler = this.tapNodes.get(node);
|
||||||
|
handler(event, node);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (let [selector, handler] of this.tapHandler.entries()) {
|
||||||
|
console.log('nodeTapped', selector);
|
||||||
|
for (let obj of this.domNode.querySelectorAll(selector)) {
|
||||||
|
if (node == obj) {
|
||||||
|
handler(event, node);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
tap(event, calledBy = 'unknown') {
|
||||||
|
if (event.isTrusted) {
|
||||||
|
let node = this.nearestActive(event);
|
||||||
|
console.log('tap', node);
|
||||||
|
this.nodeTapped(node, event);
|
||||||
|
|
||||||
|
/* let node = document.elementFromPoint(event.clientX, event.clientY)
|
||||||
|
if (!this.nodeTapped(node, event)) {
|
||||||
|
node = this.nearestActive(event)
|
||||||
|
this.nodeTapped(node, event)
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTap(objOrSelector, handler) {
|
||||||
|
if (typeof objOrSelector == 'string') {
|
||||||
|
this.tapHandler.set(objOrSelector, handler);
|
||||||
|
} else {
|
||||||
|
this.tapNodes.set(objOrSelector, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
class CardLoader {
|
class CardLoader {
|
||||||
@ -8213,20 +8408,23 @@
|
|||||||
this.backBtn = element.querySelector('.backBtn');
|
this.backBtn = element.querySelector('.backBtn');
|
||||||
this.closeBtn = element.querySelector('.closeBtn');
|
this.closeBtn = element.querySelector('.closeBtn');
|
||||||
/* Buttons are not guaranteed to exist. */
|
/* Buttons are not guaranteed to exist. */
|
||||||
|
if (scatter.tapDelegate == null) {
|
||||||
|
let tapDelegate = new CardWrapper(element);
|
||||||
|
scatter.tapDelegate = tapDelegate;
|
||||||
|
}
|
||||||
if (this.infoBtn) {
|
if (this.infoBtn) {
|
||||||
scatter.addTapListener(this.infoBtn, event => {
|
scatter.tapDelegate.onTap(this.infoBtn, event => {
|
||||||
this.flip.start();
|
this.flip.start();
|
||||||
});
|
});
|
||||||
this.enable(this.infoBtn);
|
this.enable(this.infoBtn);
|
||||||
}
|
}
|
||||||
if (this.backBtn) {
|
if (this.backBtn) {
|
||||||
scatter.addTapListener(this.backBtn, event => {
|
scatter.tapDelegate.onTap(this.backBtn, event => {
|
||||||
this.start();
|
this.start();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.closeBtn) {
|
if (this.closeBtn) {
|
||||||
scatter.addTapListener(this.closeBtn, event => {
|
scatter.tapDelegate.onTap(this.closeBtn, event => {
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
this.enable(this.closeBtn);
|
this.enable(this.closeBtn);
|
||||||
|
@ -155,7 +155,7 @@ export default class CardWrapper extends Object {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (this.tapNodes.has(node)) {
|
if (this.tapNodes.has(node)) {
|
||||||
handler = this.tapNodes.get(node)
|
let handler = this.tapNodes.get(node)
|
||||||
handler(event, node)
|
handler(event, node)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/* global PDFJS Power1 */
|
/* global PDFJS Power1 */
|
||||||
import { getId } from './utils.js'
|
import { getId } from './utils.js'
|
||||||
import { DOMScatter } from './scatter.js'
|
import { DOMScatter } from './scatter.js'
|
||||||
|
import CardWrapper from './card/wrapper.js'
|
||||||
|
|
||||||
export class CardLoader {
|
export class CardLoader {
|
||||||
constructor(
|
constructor(
|
||||||
@ -403,20 +404,23 @@ export class DOMFlippable {
|
|||||||
this.backBtn = element.querySelector('.backBtn')
|
this.backBtn = element.querySelector('.backBtn')
|
||||||
this.closeBtn = element.querySelector('.closeBtn')
|
this.closeBtn = element.querySelector('.closeBtn')
|
||||||
/* Buttons are not guaranteed to exist. */
|
/* Buttons are not guaranteed to exist. */
|
||||||
|
if (scatter.tapDelegate == null) {
|
||||||
|
let tapDelegate = new CardWrapper(element)
|
||||||
|
scatter.tapDelegate = tapDelegate
|
||||||
|
}
|
||||||
if (this.infoBtn) {
|
if (this.infoBtn) {
|
||||||
scatter.addTapListener(this.infoBtn, event => {
|
scatter.tapDelegate.onTap(this.infoBtn, event => {
|
||||||
this.flip.start()
|
this.flip.start()
|
||||||
})
|
})
|
||||||
this.enable(this.infoBtn)
|
this.enable(this.infoBtn)
|
||||||
}
|
}
|
||||||
if (this.backBtn) {
|
if (this.backBtn) {
|
||||||
scatter.addTapListener(this.backBtn, event => {
|
scatter.tapDelegate.onTap(this.backBtn, event => {
|
||||||
this.start()
|
this.start()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this.closeBtn) {
|
if (this.closeBtn) {
|
||||||
scatter.addTapListener(this.closeBtn, event => {
|
scatter.tapDelegate.onTap(this.closeBtn, event => {
|
||||||
this.close()
|
this.close()
|
||||||
})
|
})
|
||||||
this.enable(this.closeBtn)
|
this.enable(this.closeBtn)
|
||||||
|
@ -774,12 +774,13 @@ export class AbstractScatter extends Throwable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEnd(event, interaction) {
|
onEnd(event, interaction) {
|
||||||
//console.log("Scatter.onEnd", this.dragging)
|
console.log("Scatter.onEnd", this.dragging)
|
||||||
if (interaction.isFinished()) {
|
if (interaction.isFinished()) {
|
||||||
this.endGesture(interaction)
|
this.endGesture(interaction)
|
||||||
this.dragging = false
|
this.dragging = false
|
||||||
for (let key of interaction.ended.keys()) {
|
for (let key of interaction.ended.keys()) {
|
||||||
if (interaction.isTap(key)) {
|
if (interaction.isTap(key)) {
|
||||||
|
console.log("Scatter.isTap")
|
||||||
let point = interaction.ended.get(key)
|
let point = interaction.ended.get(key)
|
||||||
this.onTap(event, interaction, point)
|
this.onTap(event, interaction, point)
|
||||||
}
|
}
|
||||||
@ -804,7 +805,16 @@ export class AbstractScatter extends Throwable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTap(event, interaction, point) {}
|
//onTap(event, interaction, point) {}
|
||||||
|
|
||||||
|
onTap(event, interaction, point) {
|
||||||
|
console.log("AbstractScatter.onTap", this.tapDelegate, interaction)
|
||||||
|
if (this.tapDelegate) {
|
||||||
|
Events.stop(event)
|
||||||
|
this.tapDelegate.tap(event, 'scatter')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onDragUpdate(delta) {
|
onDragUpdate(delta) {
|
||||||
if (this.onTransform != null) {
|
if (this.onTransform != null) {
|
||||||
@ -1351,13 +1361,6 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ })
|
TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ })
|
||||||
}
|
}
|
||||||
|
|
||||||
onTap(event, interaction, point) {
|
|
||||||
if (this.tapDelegate) {
|
|
||||||
Events.stop(event)
|
|
||||||
this.tapDelegate.tap(event, 'scatter')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isDescendant(parent, child) {
|
isDescendant(parent, child) {
|
||||||
let node = child.parentNode
|
let node = child.parentNode
|
||||||
while (node != null) {
|
while (node != null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user