/* eslint-disable no-console */ /* global TweenLite */ import Events from '../events.js' import { Points } from '../utils.js' export 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 => { console.log('handleClicks', event.isTrusted) /* Currently we cannot send synthesized click events to SVG elements without unwanted side effects. Therefore we make an exception and let the original click event through. */ if (event.isTrusted) { Events.stop(event) if (this.triggerSVGClicks && this.isSVGNode(event.target)) { this.tap(event, "triggerSVGClicks") } } }, true) } handleClicksAsTaps() { this.domNode.addEventListener('click', event => { console.log('handleClicksAsTaps', event.isTrusted) /* Currently we cannot send synthesized click events to SVG elements without unwanted side effects. Therefore we make an exception and let the original click event through. */ if (event.isTrusted) { Events.stop(event) } this.tap(event) }, true) } isClickable(node) { if (node == null) 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()) { console.log("selector", this.domNode.querySelectorAll(selector)) for (let obj of this.domNode.querySelectorAll(selector)) { console.log("selector2", node, obj) 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). */ console.log("simulateClick", node, node.ownerSVGElement) if (this.isSVGNode(node)) { if (this.triggerSVGClicks) { let click = new Event('click') node.dispatchEvent(click) } return } node.click() } nodeTapped(node, event) { if (this.isClickable(node)) { this.simulateClick(node, event) return true } if (this.tapNodes.has(node)) { handler = this.tapNodes.get(node) handler(event) return true } for (let [selector, handler] of this.tapHandler.entries()) { for (let obj of this.domNode.querySelectorAll(selector)) { if (node == obj) { handler(event) return true } } } return false } tap(event, calledBy='unknown') { console.log("tap", calledBy, event.alreadyTapped, event) if (event.isTrusted) { let node = this.nearestActive(event) 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) } } } window.CardWrapper = CardWrapper