1206 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1206 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
/* eslint-disable no-unused-vars */
 | 
						|
/* globals Hammer, propagating */
 | 
						|
/*eslint no-console: ["error", { allow: ["log", "warn", "info", "error"] }]*/
 | 
						|
 | 
						|
import Interface from './interface.js'
 | 
						|
import { Points, MapProxy } from './utils.js'
 | 
						|
import Events from './events.js'
 | 
						|
import Logging from './logging.js'
 | 
						|
 | 
						|
/** Interaction patterns
 | 
						|
 | 
						|
    See interaction.html for explanation
 | 
						|
*/
 | 
						|
 | 
						|
export class IInteractionTarget extends Interface {
 | 
						|
    capture(event) {
 | 
						|
        return typeof true
 | 
						|
    }
 | 
						|
 | 
						|
    onStart(event, interaction) {}
 | 
						|
    onMove(event, interaction) {}
 | 
						|
    onEnd(event, interaction) {}
 | 
						|
 | 
						|
    onMouseWheel(event) {}
 | 
						|
}
 | 
						|
 | 
						|
export class IInteractionMapperTarget extends Interface {
 | 
						|
    capture(event) {
 | 
						|
        return typeof true
 | 
						|
    }
 | 
						|
 | 
						|
    findTarget(event, local, global) {
 | 
						|
        return IInteractionTarget
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class PointMap extends MapProxy {
 | 
						|
    // Collects touch points, mouse coordinates, etc. as key value pairs.
 | 
						|
    // Keys are pointer and touch ids, the special "mouse" key.
 | 
						|
    // Values are points, i.e. all objects with numeric x and y properties.
 | 
						|
    constructor(points = {}) {
 | 
						|
        super()
 | 
						|
        for (let key in points) {
 | 
						|
            this.set(key, points[key])
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    toString() {
 | 
						|
        let points = []
 | 
						|
        for (let key of this.keys()) {
 | 
						|
            let value = this.get(key)
 | 
						|
            points.push(`${key}:{x:${value.x}, y:${value.y}}`)
 | 
						|
        }
 | 
						|
        let attrs = points.join(', ')
 | 
						|
        return `[PointMap ${attrs}]`
 | 
						|
    }
 | 
						|
 | 
						|
    clone() {
 | 
						|
        let result = new PointMap()
 | 
						|
        for (let key of this.keys()) {
 | 
						|
            let value = this.get(key)
 | 
						|
            result.set(key, { x: value.x, y: value.y })
 | 
						|
        }
 | 
						|
        return result
 | 
						|
    }
 | 
						|
 | 
						|
    keyOf(value) {
 | 
						|
        for (let key of this.keys()) {
 | 
						|
            let p = this.get(key)
 | 
						|
            if (p.x == value.x && p.y == value.y) {
 | 
						|
                return key
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return null
 | 
						|
    }
 | 
						|
 | 
						|
    firstKey() {
 | 
						|
        for (let key of this.keys()) {
 | 
						|
            return key
 | 
						|
        }
 | 
						|
        return null
 | 
						|
    }
 | 
						|
 | 
						|
    first() {
 | 
						|
        for (let key of this.keys()) {
 | 
						|
            return this.get(key)
 | 
						|
        }
 | 
						|
        return null
 | 
						|
    }
 | 
						|
 | 
						|
    farthests() {
 | 
						|
        if (this.size == 0) {
 | 
						|
            return null
 | 
						|
        }
 | 
						|
        let pairs = []
 | 
						|
        for (let key of this.keys()) {
 | 
						|
            let p = this.get(key)
 | 
						|
            p.key = key
 | 
						|
            for (let k of this.keys()) {
 | 
						|
                let q = this.get(k)
 | 
						|
                q.key = k
 | 
						|
                pairs.push([p, q])
 | 
						|
            }
 | 
						|
        }
 | 
						|
        let sorted = pairs.sort((a, b) => {
 | 
						|
            return Points.distance(b[0], b[1]) - Points.distance(a[0], a[1])
 | 
						|
        })
 | 
						|
        return sorted[0]
 | 
						|
    }
 | 
						|
 | 
						|
    mean() {
 | 
						|
        if (this.size == 0) {
 | 
						|
            return null
 | 
						|
        }
 | 
						|
        let x = 0.0,
 | 
						|
            y = 0.0
 | 
						|
        for (let p of this.values()) {
 | 
						|
            x += p.x
 | 
						|
            y += p.y
 | 
						|
        }
 | 
						|
        return { x: x / this.size, y: y / this.size }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class InteractionDelta {
 | 
						|
    /**
 | 
						|
     *Creates an instance of InteractionDelta.
 | 
						|
     * @param {*} x
 | 
						|
     * @param {*} y
 | 
						|
     * @param {*} zoom
 | 
						|
     * @param {*} rotate
 | 
						|
     * @param {*} about
 | 
						|
     * @param {*} number - number of involved pointer
 | 
						|
     * @param {*} distance - distance of farthests touch points
 | 
						|
     * @memberof InteractionDelta
 | 
						|
     */
 | 
						|
    constructor(x, y, zoom, rotate, about, number, distance) {
 | 
						|
        this.x = x
 | 
						|
        this.y = y
 | 
						|
        this.zoom = zoom
 | 
						|
        this.rotate = rotate
 | 
						|
        this.about = about
 | 
						|
        this.number = number
 | 
						|
        this.distance = distance
 | 
						|
    }
 | 
						|
 | 
						|
    toString() {
 | 
						|
        let values = []
 | 
						|
        for (let key of Object.keys(this)) {
 | 
						|
            let value = this[key]
 | 
						|
            if (key == 'about') {
 | 
						|
                values.push(`${key}:{x:${value.x}, y:${value.y}}`)
 | 
						|
            } else {
 | 
						|
                values.push(`${key}:${value}`)
 | 
						|
            }
 | 
						|
        }
 | 
						|
        let attrs = values.join(', ')
 | 
						|
        return `[InteractionDelta ${attrs}]`
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class InteractionPoints {
 | 
						|
    constructor(parent = null) {
 | 
						|
        this.parent = parent
 | 
						|
        this.current = new PointMap()
 | 
						|
        this.previous = new PointMap()
 | 
						|
        this.start = new PointMap()
 | 
						|
        this.ended = new PointMap()
 | 
						|
        this.timestamps = new Map()
 | 
						|
    }
 | 
						|
 | 
						|
    moved(key) {
 | 
						|
        let current = this.current.get(key)
 | 
						|
        let previous = this.previous.get(key)
 | 
						|
        return Points.subtract(current, previous)
 | 
						|
    }
 | 
						|
 | 
						|
    move() {
 | 
						|
        let current = this.current.mean()
 | 
						|
        let previous = this.previous.mean()
 | 
						|
        return Points.subtract(current, previous)
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Computes the delta between previous and current angles. Corrects
 | 
						|
     * value that are larger than 45°
 | 
						|
     * @param {*} a
 | 
						|
     * @param {*} b
 | 
						|
     * @returns delta
 | 
						|
     */
 | 
						|
    diffAngle(a, b) {
 | 
						|
        let alpha = Math.atan2(Math.sin(a - b), Math.cos(a - b))
 | 
						|
        if (Math.abs(alpha) > Math.PI / 4) {
 | 
						|
            alpha -= Math.PI
 | 
						|
        }
 | 
						|
        return alpha
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Computes the delta between interaction points at t and t+1.
 | 
						|
     *
 | 
						|
     * @returns InteractionDelta
 | 
						|
     * @memberof InteractionPoints
 | 
						|
     */
 | 
						|
    delta() {
 | 
						|
        let prev = []
 | 
						|
        let curr = []
 | 
						|
        let cm = { x: 0, y: 0 }
 | 
						|
        let pm = { x: 0, y: 0 }
 | 
						|
        let count = 0
 | 
						|
        for (let key of this.current.keys()) {
 | 
						|
            if (this.previous.has(key)) {
 | 
						|
                let p = this.previous.get(key)
 | 
						|
                let c = this.current.get(key)
 | 
						|
                pm = Points.add(pm, p)
 | 
						|
                cm = Points.add(cm, c)
 | 
						|
                prev.push(p)
 | 
						|
                curr.push(c)
 | 
						|
                count += 1
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (count > 0) {
 | 
						|
            pm = Points.multiplyScalar(pm, 1 / count)
 | 
						|
            cm = Points.multiplyScalar(cm, 1 / count)
 | 
						|
            let delta = Points.subtract(cm, pm)
 | 
						|
            let scale = 0
 | 
						|
            let scaled = 0
 | 
						|
            let alpha = 0
 | 
						|
            let zoom = 1
 | 
						|
            for (let i = 0; i < count; i++) {
 | 
						|
                let p = prev[i]
 | 
						|
                let c = curr[i]
 | 
						|
                let previousAngle = Points.angle(p, pm)
 | 
						|
                let currentAngle = Points.angle(c, cm)
 | 
						|
                let diff = this.diffAngle(currentAngle, previousAngle)
 | 
						|
                alpha += diff
 | 
						|
 | 
						|
                let distance1 = Points.distance(p, pm)
 | 
						|
                let distance2 = Points.distance(c, cm)
 | 
						|
                if (distance1 != 0 && distance2 != 0) {
 | 
						|
                    scale += distance2 / distance1
 | 
						|
                    scaled += 1
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (scaled > 0) {
 | 
						|
                zoom = scale / scaled
 | 
						|
            }
 | 
						|
            alpha /= count
 | 
						|
 | 
						|
            let current = this.current.farthests()
 | 
						|
 | 
						|
            let c1 = current[0]
 | 
						|
            let c2 = current[1]
 | 
						|
            let distance2 = Points.distance(c1, c2)
 | 
						|
 | 
						|
            return new InteractionDelta(delta.x, delta.y, zoom, alpha, cm, count, distance2)
 | 
						|
        } else {
 | 
						|
            return null
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Computes the delta between interaction points at t and t+1.
 | 
						|
     *
 | 
						|
     * @returns InteractionDelta
 | 
						|
     * @memberof InteractionPoints
 | 
						|
     */
 | 
						|
    deltaByTwoFarthestsPoints() {
 | 
						|
        let csize = this.current.size
 | 
						|
        let psize = this.previous.size
 | 
						|
        if (csize >= 2 && csize == psize) {
 | 
						|
            // Reduce to the two farthests points
 | 
						|
            let current = this.current.farthests()
 | 
						|
 | 
						|
            let c1 = current[0]
 | 
						|
            let c2 = current[1]
 | 
						|
 | 
						|
            let p1 = this.previous.get(c1.key)
 | 
						|
            let p2 = this.previous.get(c2.key)
 | 
						|
 | 
						|
            let d1 = Points.subtract(c1, p1)
 | 
						|
            let d2 = Points.subtract(c2, p2)
 | 
						|
            let cm = Points.mean(c1, c2)
 | 
						|
 | 
						|
            // Using the mean leads to jumps between time slices with 3 and 2 fingers
 | 
						|
            // We use the mean of deltas instead
 | 
						|
            let delta = Points.mean(d1, d2)
 | 
						|
            let zoom = 1.0
 | 
						|
            let distance1 = Points.distance(p1, p2)
 | 
						|
            let distance2 = Points.distance(c1, c2)
 | 
						|
            if (distance1 != 0 && distance2 != 0) {
 | 
						|
                zoom = distance2 / distance1
 | 
						|
            }
 | 
						|
            let currentAngle = Points.angle(c1, c2)
 | 
						|
            let previousAngle = Points.angle(p1, p2)
 | 
						|
            let alpha = this.diffAngle(currentAngle, previousAngle)
 | 
						|
            return new InteractionDelta(delta.x, delta.y, zoom, alpha, cm, csize, distance2)
 | 
						|
        } else if (csize == 1 && psize == 1 && this.current.firstKey() == this.previous.firstKey()) {
 | 
						|
            // We need to ensure that the keys are the same, since single points with different keys
 | 
						|
            // can jump
 | 
						|
            let current = this.current.first()
 | 
						|
            let previous = this.previous.first()
 | 
						|
            let delta = Points.subtract(current, previous)
 | 
						|
            return new InteractionDelta(delta.x, delta.y, 1.0, 0.0, current, csize)
 | 
						|
        }
 | 
						|
        return null
 | 
						|
    }
 | 
						|
 | 
						|
    started(key, point) {
 | 
						|
        this.current.set(key, point)
 | 
						|
        this.start.set(key, point)
 | 
						|
        this.previous.set(key, point)
 | 
						|
        this.timestamps.set(key, performance.now())
 | 
						|
    }
 | 
						|
 | 
						|
    update(key, point) {
 | 
						|
        // Returns true iff the key is new
 | 
						|
        this.current.set(key, point)
 | 
						|
        if (!this.start.has(key)) {
 | 
						|
            this.start.set(key, point)
 | 
						|
            this.previous.set(key, point)
 | 
						|
            this.timestamps.set(key, performance.now())
 | 
						|
            return true
 | 
						|
        }
 | 
						|
        return false
 | 
						|
    }
 | 
						|
 | 
						|
    updatePrevious() {
 | 
						|
        for (let key of this.current.keys()) {
 | 
						|
            this.previous.set(key, this.current.get(key))
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    stop(key, point) {
 | 
						|
        if (this.current.has(key)) {
 | 
						|
            this.current.delete(key)
 | 
						|
            this.previous.delete(key)
 | 
						|
            this.ended.set(key, point)
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    finish(key, point) {
 | 
						|
        this.current.delete(key)
 | 
						|
        this.previous.delete(key)
 | 
						|
        this.start.delete(key)
 | 
						|
        this.timestamps.delete(key)
 | 
						|
        this.ended.delete(key)
 | 
						|
    }
 | 
						|
 | 
						|
    isFinished() {
 | 
						|
        return this.current.size == 0
 | 
						|
    }
 | 
						|
 | 
						|
    isNoLongerTwoFinger() {
 | 
						|
        return this.previous.size > 1 && this.current.size < 2
 | 
						|
    }
 | 
						|
 | 
						|
    isTap(key) {
 | 
						|
        return this.parent.isTap(key)
 | 
						|
    }
 | 
						|
 | 
						|
    isDoubleTap(key) {
 | 
						|
        return this.parent.isDoubleTap(key)
 | 
						|
    }
 | 
						|
 | 
						|
    isLongPress(key) {
 | 
						|
        return this.parent.isLongPress(key)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class Interaction extends InteractionPoints {
 | 
						|
    constructor(tapDistance = 10, tapDuration = 250.0, longPressTime = 500.0) {
 | 
						|
        super()
 | 
						|
        this.tapDistance = tapDistance
 | 
						|
        this.tapCounts = new Map()
 | 
						|
        this.tapPositions = new Map()
 | 
						|
        this.tapTimestamps = new Map()
 | 
						|
        this.tapDuration = tapDuration
 | 
						|
        this.longPressTime = longPressTime
 | 
						|
        this.targets = new Map()
 | 
						|
        this.subInteractions = new Map() // target:Object : InteractionPoints
 | 
						|
    }
 | 
						|
 | 
						|
    stop(key, point) {
 | 
						|
        super.stop(key, point)
 | 
						|
        for (let points of this.subInteractions.values()) {
 | 
						|
            points.stop(key, point)
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    addTarget(key, target) {
 | 
						|
        this.targets.set(key, target)
 | 
						|
        this.subInteractions.set(target, new InteractionPoints(this))
 | 
						|
    }
 | 
						|
 | 
						|
    removeTarget(key) {
 | 
						|
        let target = this.targets.get(key)
 | 
						|
        this.targets.delete(key)
 | 
						|
        // Only remove target if no keys are refering to the target
 | 
						|
        let remove = true
 | 
						|
        for (let t of this.targets.values()) {
 | 
						|
            if (target === t) {
 | 
						|
                remove = false
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (remove) {
 | 
						|
            this.subInteractions.delete(target)
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    finish(key, point) {
 | 
						|
        super.finish(key, point)
 | 
						|
        this.removeTarget(key)
 | 
						|
    }
 | 
						|
 | 
						|
    mapInteraction(points, aspects, mappingFunc) {
 | 
						|
        // Map centrally registered points to target interactions
 | 
						|
        // Returns an array of [target, updated subInteraction] pairs
 | 
						|
        let result = new Map()
 | 
						|
        for (let key in points) {
 | 
						|
            if (this.targets.has(key)) {
 | 
						|
                let target = this.targets.get(key)
 | 
						|
                if (this.subInteractions.has(target)) {
 | 
						|
                    let interaction = this.subInteractions.get(target)
 | 
						|
                    for (let aspect of aspects) {
 | 
						|
                        let pointMap = this[aspect]
 | 
						|
                        let point = pointMap.get(key)
 | 
						|
                        let mapped = mappingFunc(point, target)
 | 
						|
                        interaction[aspect].set(key, mapped)
 | 
						|
                    }
 | 
						|
                    result.set(target, interaction)
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return result
 | 
						|
    }
 | 
						|
 | 
						|
    registerTap(key, point) {
 | 
						|
        if (this.tapCounts.has(key)) {
 | 
						|
            let count = this.tapCounts.get(key)
 | 
						|
            this.tapCounts.set(key, count + 1)
 | 
						|
        } else {
 | 
						|
            this.tapCounts.set(key, 1)
 | 
						|
        }
 | 
						|
        this.tapPositions.set(key, point)
 | 
						|
        this.tapTimestamps.set(key, performance.now())
 | 
						|
    }
 | 
						|
 | 
						|
    unregisterTap(key) {
 | 
						|
        this.tapCounts.delete(key)
 | 
						|
        this.tapPositions.delete(key)
 | 
						|
        this.tapTimestamps.delete(key)
 | 
						|
    }
 | 
						|
 | 
						|
    isTap(key) {
 | 
						|
        let ended = this.ended.get(key)
 | 
						|
        let start = this.start.get(key)
 | 
						|
        if (start && ended && Points.distance(ended, start) < this.tapDistance) {
 | 
						|
            let t1 = this.timestamps.get(key)
 | 
						|
            let tookLong = performance.now() > t1 + this.longPressTime
 | 
						|
            if (tookLong) {
 | 
						|
                return false
 | 
						|
            }
 | 
						|
            return true
 | 
						|
        }
 | 
						|
        return false
 | 
						|
    }
 | 
						|
 | 
						|
    isDoubleTap(key) {
 | 
						|
        let ended = this.ended.get(key)
 | 
						|
        if (this.tapCounts.has(key) && this.tapCounts.get(key) > 2) {
 | 
						|
            this.unregisterTap(key)
 | 
						|
        }
 | 
						|
        if (this.tapPositions.has(key)) {
 | 
						|
            let pos = this.tapPositions.get(key)
 | 
						|
            if (Points.distance(ended, pos) > this.tapDistance) {
 | 
						|
                this.unregisterTap(key)
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (this.tapTimestamps.has(key) && performance.now() > this.tapTimestamps.get(key) + this.tapDuration) {
 | 
						|
            //console.log("tap too long")
 | 
						|
            this.unregisterTap(key)
 | 
						|
        }
 | 
						|
        let result = false
 | 
						|
        if (this.isTap(key)) {
 | 
						|
            this.registerTap(key, ended)
 | 
						|
            result = this.tapCounts.get(key) == 2
 | 
						|
        } else {
 | 
						|
            this.unregisterTap(key)
 | 
						|
        }
 | 
						|
        //console.log("isDoubleTap", this.tapCounts.get(key), result)
 | 
						|
        return result
 | 
						|
    }
 | 
						|
 | 
						|
    isAnyTap() {
 | 
						|
        for (let key of this.ended.keys()) {
 | 
						|
            if (this.isTap(key)) return true
 | 
						|
        }
 | 
						|
        return false
 | 
						|
    }
 | 
						|
 | 
						|
    isLongPress(key) {
 | 
						|
        let ended = this.ended.get(key)
 | 
						|
        let start = this.start.get(key)
 | 
						|
        if (start && ended && Points.distance(ended, start) < this.tapDistance) {
 | 
						|
            let t1 = this.timestamps.get(key)
 | 
						|
            let tookLong = performance.now() > t1 + this.longPressTime
 | 
						|
            if (tookLong) {
 | 
						|
                return true
 | 
						|
            }
 | 
						|
            return false
 | 
						|
        }
 | 
						|
        return false
 | 
						|
    }
 | 
						|
 | 
						|
    isAnyLongPress() {
 | 
						|
        for (let key of this.ended.keys()) {
 | 
						|
            if (this.isLongPress(key)) return true
 | 
						|
        }
 | 
						|
        return false
 | 
						|
    }
 | 
						|
 | 
						|
    isStylus(key) {
 | 
						|
        return key === 'stylus'
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * This class implements the main delegate functionality: All necessary event handlers are registered for the
 | 
						|
 * given element. Uses PointerEvents if available or TouchEvents on iOS. The fallback is on mouse events.
 | 
						|
 * Collects the events if the interaction target captures the start event (i.e. declares that
 | 
						|
 * the target wants the start event as well as all following move and end evcents.)
 | 
						|
 *
 | 
						|
 * @export
 | 
						|
 * @class InteractionDelegate
 | 
						|
 */
 | 
						|
export class InteractionDelegate {
 | 
						|
    // Long press: http://stackoverflow.com/questions/1930895/how-long-is-the-event-onlongpress-in-the-android
 | 
						|
    // Stylus support: https://w3c.github.io/touch-events/
 | 
						|
 | 
						|
    /**
 | 
						|
     * Creates an instance of InteractionDelegate.
 | 
						|
     * @param {any} element
 | 
						|
     * @param {any} target
 | 
						|
     * @param {any} [{ mouseWheelElement = null, useCapture = true, capturePointerEvents = true, debug = false }={}]
 | 
						|
     * @memberof InteractionDelegate
 | 
						|
     */
 | 
						|
    constructor(
 | 
						|
        element,
 | 
						|
        target,
 | 
						|
        {
 | 
						|
            mouseWheelElement = null,
 | 
						|
            useCapture = true,
 | 
						|
            capturePointerEvents = true,
 | 
						|
            cancelOnWindowOut = true,
 | 
						|
            preventPointerClicks = true,
 | 
						|
            debug = false
 | 
						|
        } = {}
 | 
						|
    ) {
 | 
						|
        this.debug = debug
 | 
						|
        this.interaction = new Interaction()
 | 
						|
        this.element = element
 | 
						|
        this.mouseWheelElement = mouseWheelElement || element
 | 
						|
        this.target = target
 | 
						|
        this.useCapture = useCapture
 | 
						|
        this.capturePointerEvents = capturePointerEvents
 | 
						|
        this.cancelOnWindowOut = cancelOnWindowOut
 | 
						|
        this.preventPointerClicks = preventPointerClicks
 | 
						|
        this.setupInteraction()
 | 
						|
    }
 | 
						|
 | 
						|
    setupInteraction() {
 | 
						|
        if (this.debug) {
 | 
						|
            let error = this.targetInterface.implementationError(this.target.constructor)
 | 
						|
            if (error != null) {
 | 
						|
                throw new Error('Expected IInteractionTarget: ' + error)
 | 
						|
            }
 | 
						|
        }
 | 
						|
        this.setupTouchInteraction()
 | 
						|
        this.setupMouseWheelInteraction()
 | 
						|
    }
 | 
						|
 | 
						|
    get targetInterface() {
 | 
						|
        return IInteractionTarget
 | 
						|
    }
 | 
						|
 | 
						|
    setupTouchInteraction() {
 | 
						|
        let element = this.element
 | 
						|
        let useCapture = this.useCapture
 | 
						|
        if (window.PointerEvent) {
 | 
						|
            if (this.debug) console.log('Pointer API' + window.PointerEvent)
 | 
						|
            element.addEventListener(
 | 
						|
                'pointerdown',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('pointerdown', e.pointerId)
 | 
						|
                    if (this.capture(e)) {
 | 
						|
                        if (this.capturePointerEvents) {
 | 
						|
                            try {
 | 
						|
                                element.setPointerCapture(e.pointerId)
 | 
						|
                            } catch (e) {
 | 
						|
                                console.warn('Cannot setPointerCapture', e.setPointerCapture)
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        this.onStart(e)
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
            element.addEventListener(
 | 
						|
                'pointermove',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('pointermove', e.pointerId, e.pointerType)
 | 
						|
 | 
						|
                    if (e.pointerType == 'touch' || (e.pointerType == 'mouse' && Events.isPointerDown(e))) {
 | 
						|
                        // this.capture(e) &&
 | 
						|
                        if (this.debug) console.log('pointermove captured', e.pointerId)
 | 
						|
                        this.onMove(e)
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
            element.addEventListener(
 | 
						|
                'pointerup',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('pointerup', e.pointerId, e.pointerType)
 | 
						|
                    this.onEnd(e)
 | 
						|
                    if (this.capturePointerEvents) {
 | 
						|
                        try {
 | 
						|
                            element.releasePointerCapture(e.pointerId)
 | 
						|
                        } catch (e) {
 | 
						|
                            console.warn('Cannot release pointer')
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
            element.addEventListener(
 | 
						|
                'pointercancel',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('pointercancel', e.pointerId, e.pointerType)
 | 
						|
                    this.onEnd(e)
 | 
						|
                    if (this.capturePointerEvents) element.releasePointerCapture(e.pointerId)
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
 | 
						|
            if (!this.capturePointerEvents) {
 | 
						|
                element.addEventListener(
 | 
						|
                    'pointerleave',
 | 
						|
                    e => {
 | 
						|
                        if (this.debug) console.log('pointerleave', e.pointerId, e.pointerType)
 | 
						|
                        if (e.target == element) this.onEnd(e)
 | 
						|
                    },
 | 
						|
                    useCapture
 | 
						|
                )
 | 
						|
            }
 | 
						|
 | 
						|
            if (!this.capturePointerEvents) {
 | 
						|
                element.addEventListener(
 | 
						|
                    'pointerout',
 | 
						|
                    e => {
 | 
						|
                        if (this.debug) console.log('pointerout', e.pointerId, e.pointerType)
 | 
						|
                        if (e.target == element) this.onEnd(e)
 | 
						|
                    },
 | 
						|
                    useCapture
 | 
						|
                )
 | 
						|
            }
 | 
						|
 | 
						|
            if (this.cancelOnWindowOut) {
 | 
						|
                window.addEventListener(
 | 
						|
                    'pointerout',
 | 
						|
                    e => {
 | 
						|
                        if (this.debug) console.log('pointerout', e.pointerId, e.pointerType, e.target)
 | 
						|
                        if (e.target == element) {
 | 
						|
                            this.onEnd(e)
 | 
						|
                        }
 | 
						|
                    },
 | 
						|
                    useCapture
 | 
						|
                )
 | 
						|
            }
 | 
						|
        } else if (window.TouchEvent) {
 | 
						|
            if (this.debug) console.log('Touch API')
 | 
						|
            element.addEventListener(
 | 
						|
                'touchstart',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('touchstart', this.touchPoints(e))
 | 
						|
                    if (this.capture(e)) {
 | 
						|
                        for (let touch of e.changedTouches) {
 | 
						|
                            this.onStart(touch)
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
            element.addEventListener(
 | 
						|
                'touchmove',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('touchmove', this.touchPoints(e), e)
 | 
						|
                    for (let touch of e.changedTouches) {
 | 
						|
                        this.onMove(touch)
 | 
						|
                    }
 | 
						|
                    for (let touch of e.targetTouches) {
 | 
						|
                        this.onMove(touch)
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
            element.addEventListener(
 | 
						|
                'touchend',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('touchend', this.touchPoints(e))
 | 
						|
                    for (let touch of e.changedTouches) {
 | 
						|
                        this.onEnd(touch)
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
            element.addEventListener(
 | 
						|
                'touchcancel',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('touchcancel', e.targetTouches.length, e.changedTouches.length)
 | 
						|
                    for (let touch of e.changedTouches) {
 | 
						|
                        this.onEnd(touch)
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
        } else {
 | 
						|
            if (this.debug) console.log('Mouse API')
 | 
						|
 | 
						|
            element.addEventListener(
 | 
						|
                'mousedown',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('mousedown', e)
 | 
						|
                    if (this.capture(e)) {
 | 
						|
                        this.onStart(e)
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
            element.addEventListener(
 | 
						|
                'mousemove',
 | 
						|
                e => {
 | 
						|
                    // Dow we only use move events if the mouse is down?
 | 
						|
                    // HOver effects have to be implemented by other means
 | 
						|
                    // && Events.isMouseDown(e))
 | 
						|
 | 
						|
                    if (Events.isMouseDown(e)) {
 | 
						|
                        if (this.debug) console.log('mousemove', e)
 | 
						|
                        this.onMove(e)
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                useCapture
 | 
						|
            )
 | 
						|
            element.addEventListener(
 | 
						|
                'mouseup',
 | 
						|
                e => {
 | 
						|
                    if (this.debug) console.log('mouseup', e)
 | 
						|
                    this.onEnd(e)
 | 
						|
                },
 | 
						|
                true
 | 
						|
            )
 | 
						|
 | 
						|
            if (!this.capturePointerEvents) {
 | 
						|
                element.addEventListener(
 | 
						|
                    'mouseout',
 | 
						|
                    e => {
 | 
						|
                        if (e.target == element) {
 | 
						|
                            this.onEnd(e)
 | 
						|
                            console.warn("Shouldn't happen: mouseout ends interaction")
 | 
						|
                        }
 | 
						|
                    },
 | 
						|
                    useCapture
 | 
						|
                )
 | 
						|
            }
 | 
						|
            if (this.cancelOnWindowOut) {
 | 
						|
                window.addEventListener(
 | 
						|
                    'mouseout',
 | 
						|
                    e => {
 | 
						|
                        if (e.target == element) {
 | 
						|
                            this.onEnd(e)
 | 
						|
                        }
 | 
						|
                    },
 | 
						|
                    useCapture
 | 
						|
                )
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (this.preventPointerClicks) {
 | 
						|
            window.addEventListener(
 | 
						|
                'click',
 | 
						|
                e => {
 | 
						|
                    if (e instanceof PointerEvent) {
 | 
						|
                        console.warn('Prevented pointer click')
 | 
						|
                        e.preventDefault()
 | 
						|
                        e.stopImmediatePropagation()
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                true
 | 
						|
            )
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    isDescendant(parent, child) {
 | 
						|
        if (parent == child) return true
 | 
						|
        let node = child.parentNode
 | 
						|
        while (node != null) {
 | 
						|
            if (node == parent) {
 | 
						|
                return true
 | 
						|
            }
 | 
						|
            node = node.parentNode
 | 
						|
        }
 | 
						|
        return false
 | 
						|
    }
 | 
						|
 | 
						|
    touchPoints(event) {
 | 
						|
        let result = []
 | 
						|
        for (let touch of event.changedTouches) {
 | 
						|
            result.push(this.extractPoint(touch))
 | 
						|
        }
 | 
						|
        return result
 | 
						|
    }
 | 
						|
 | 
						|
    setupMouseWheelInteraction() {
 | 
						|
        this.mouseWheelElement.addEventListener('mousewheel', this.onMouseWheel.bind(this), true)
 | 
						|
        this.mouseWheelElement.addEventListener('DOMMouseScroll', this.onMouseWheel.bind(this), true)
 | 
						|
    }
 | 
						|
 | 
						|
    onMouseWheel(event) {
 | 
						|
        if (this.capture(event) && this.target.onMouseWheel) {
 | 
						|
            this.target.onMouseWheel(event)
 | 
						|
        } else {
 | 
						|
            //console.warn('Target has no onMouseWheel callback')
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    onStart(event) {
 | 
						|
        let extracted = this.extractPoint(event)
 | 
						|
        this.startInteraction(event, extracted)
 | 
						|
        this.target.onStart(event, this.interaction)
 | 
						|
    }
 | 
						|
 | 
						|
    onMove(event) {
 | 
						|
        let extracted = this.extractPoint(event, 'all')
 | 
						|
        this.updateInteraction(event, extracted)
 | 
						|
        this.target.onMove(event, this.interaction)
 | 
						|
        this.interaction.updatePrevious()
 | 
						|
    }
 | 
						|
 | 
						|
    onEnd(event) {
 | 
						|
        let extracted = this.extractPoint(event, 'changedTouches')
 | 
						|
        this.endInteraction(event, extracted)
 | 
						|
        this.target.onEnd(event, this.interaction)
 | 
						|
        this.finishInteraction(event, extracted)
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Asks the target whether the event should be captured
 | 
						|
     *
 | 
						|
     * @param {any} event
 | 
						|
     * @returns {bool}
 | 
						|
     * @memberof InteractionDelegate
 | 
						|
     */
 | 
						|
    capture(event) {
 | 
						|
        if (Events.isCaptured(event)) {
 | 
						|
            return false
 | 
						|
        }
 | 
						|
 | 
						|
        return this.target.capture ? this.target.capture(event) : false
 | 
						|
    }
 | 
						|
 | 
						|
    getPosition(event) {
 | 
						|
        return { x: event.clientX, y: event.clientY }
 | 
						|
    }
 | 
						|
 | 
						|
    extractPoint(event, touchEventKey = 'all') {
 | 
						|
        // 'targetTouches'
 | 
						|
        let result = {}
 | 
						|
        switch (event.constructor.name) {
 | 
						|
            case 'MouseEvent': {
 | 
						|
                let buttons = event.buttons || event.which
 | 
						|
                if (buttons) result['mouse'] = this.getPosition(event)
 | 
						|
                break
 | 
						|
            }
 | 
						|
            case 'PointerEvent': {
 | 
						|
                result[event.pointerId.toString()] = this.getPosition(event)
 | 
						|
                break
 | 
						|
            }
 | 
						|
            case 'Touch': {
 | 
						|
                let id = event.touchType === 'stylus' ? 'stylus' : event.identifier.toString()
 | 
						|
                result[id] = this.getPosition(event)
 | 
						|
                break
 | 
						|
            }
 | 
						|
            //             case 'TouchEvent':
 | 
						|
            //                 // Needs to be observed: Perhaps changedTouches are all we need. If so
 | 
						|
            //                 // we can remove the touchEventKey default parameter
 | 
						|
            //                 if (touchEventKey == 'all') {
 | 
						|
            //                     for(let t of event.targetTouches) {
 | 
						|
            //                         result[t.identifier.toString()] = this.getPosition(t)
 | 
						|
            //                     }
 | 
						|
            //                     for(let t of event.changedTouches) {
 | 
						|
            //                         result[t.identifier.toString()] = this.getPosition(t)
 | 
						|
            //                     }
 | 
						|
            //                 }
 | 
						|
            //                 else {
 | 
						|
            //                     for(let t of event.changedTouches) {
 | 
						|
            //                         result[t.identifier.toString()] = this.getPosition(t)
 | 
						|
            //                     }
 | 
						|
            //                 }
 | 
						|
            //                 break
 | 
						|
            default:
 | 
						|
                break
 | 
						|
        }
 | 
						|
        return result
 | 
						|
    }
 | 
						|
 | 
						|
    interactionStarted(event, key, point) {
 | 
						|
        // Callback: can be overwritten
 | 
						|
    }
 | 
						|
 | 
						|
    interactionEnded(event, key, point) {
 | 
						|
        // Callback: can be overwritten
 | 
						|
    }
 | 
						|
 | 
						|
    interactionFinished(event, key, point) {}
 | 
						|
 | 
						|
    startInteraction(event, extracted) {
 | 
						|
        for (let key in extracted) {
 | 
						|
            let point = extracted[key]
 | 
						|
            this.interaction.started(key, point)
 | 
						|
            this.interactionStarted(event, key, point)
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    updateInteraction(event, extracted) {
 | 
						|
        for (let key in extracted) {
 | 
						|
            let point = extracted[key]
 | 
						|
            let updated = this.interaction.update(key, point)
 | 
						|
            if (updated) {
 | 
						|
                console.warn("new pointer in updateInteraction shouldn't happen", key)
 | 
						|
                this.interactionStarted(event, key, point)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    endInteraction(event, ended) {
 | 
						|
        for (let key in ended) {
 | 
						|
            let point = ended[key]
 | 
						|
            this.interaction.stop(key, point)
 | 
						|
            this.interactionEnded(event, key, point)
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    finishInteraction(event, ended) {
 | 
						|
        for (let key in ended) {
 | 
						|
            let point = ended[key]
 | 
						|
            this.interaction.finish(key, point)
 | 
						|
            this.interactionFinished(event, key, point)
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * A special InteractionDelegate that maps events to specific parts of
 | 
						|
 * the interaction target. The InteractionTarget must implement a findTarget
 | 
						|
 * method that returns an object implementing the IInteractionTarget interface.
 | 
						|
 *
 | 
						|
 * If the InteractionTarget also implements a mapPositionToPoint method this
 | 
						|
 * is used to map the points to the local coordinate space of the the target.
 | 
						|
 *
 | 
						|
 * This makes it easier to lookup elements and relate events to local
 | 
						|
 * positions.
 | 
						|
 *
 | 
						|
 * @export
 | 
						|
 * @class InteractionMapper
 | 
						|
 * @extends {InteractionDelegate}
 | 
						|
 */
 | 
						|
export class InteractionMapper extends InteractionDelegate {
 | 
						|
    constructor(
 | 
						|
        element,
 | 
						|
        target,
 | 
						|
        {
 | 
						|
            tapDistance = 10,
 | 
						|
            longPressTime = 500.0,
 | 
						|
            useCapture = true,
 | 
						|
            capturePointerEvents = true,
 | 
						|
            mouseWheelElement = null,
 | 
						|
            preventPointerClicks = true,
 | 
						|
            logInteractionsAbove = 12
 | 
						|
        } = {}
 | 
						|
    ) {
 | 
						|
        super(element, target, {
 | 
						|
            tapDistance,
 | 
						|
            useCapture,
 | 
						|
            capturePointerEvents,
 | 
						|
            preventPointerClicks,
 | 
						|
            longPressTime,
 | 
						|
            mouseWheelElement
 | 
						|
        })
 | 
						|
        this.logInteractionsAbove = logInteractionsAbove
 | 
						|
    }
 | 
						|
 | 
						|
    get targetInterface() {
 | 
						|
        return IInteractionMapperTarget
 | 
						|
    }
 | 
						|
 | 
						|
    mapPositionToPoint(point, element = null) {
 | 
						|
        if (this.target.mapPositionToPoint) {
 | 
						|
            return this.target.mapPositionToPoint(point, element)
 | 
						|
        }
 | 
						|
        return point
 | 
						|
    }
 | 
						|
 | 
						|
    interactionStarted(event, key, point) {
 | 
						|
        if (this.target.findTarget) {
 | 
						|
            let local = this.mapPositionToPoint(point)
 | 
						|
            let found = this.target.findTarget(event, local, point)
 | 
						|
            if (found != null) {
 | 
						|
                this.interaction.addTarget(key, found)
 | 
						|
            }
 | 
						|
        }
 | 
						|
        let size = this.interaction.current.size
 | 
						|
        let limit = this.logInteractionsAbove
 | 
						|
        if (size > limit) {
 | 
						|
            Logging.log(`Number of interactions ${size} exceeds ${limit}`)
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    onMouseWheel(event) {
 | 
						|
        if (this.capture(event)) {
 | 
						|
            if (this.target.findTarget) {
 | 
						|
                let point = this.getPosition(event)
 | 
						|
                let local = this.mapPositionToPoint(point)
 | 
						|
                let found = this.target.findTarget(event, local, point)
 | 
						|
                if (found != null && found.onMouseWheel) {
 | 
						|
                    found.onMouseWheel(event)
 | 
						|
                    return
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (this.target.onMouseWheel) {
 | 
						|
                this.target.onMouseWheel(event)
 | 
						|
            } else {
 | 
						|
                //console.warn('Target has no onMouseWheel callback', this.target)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    onStart(event) {
 | 
						|
        let extracted = this.extractPoint(event)
 | 
						|
        this.startInteraction(event, extracted)
 | 
						|
        let mapped = this.interaction.mapInteraction(
 | 
						|
            extracted,
 | 
						|
            ['current', 'start'],
 | 
						|
            this.mapPositionToPoint.bind(this)
 | 
						|
        )
 | 
						|
        for (let [target, interaction] of mapped.entries()) {
 | 
						|
            target.onStart(event, interaction)
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    onMove(event) {
 | 
						|
        let extracted = this.extractPoint(event, 'all')
 | 
						|
        this.updateInteraction(event, extracted)
 | 
						|
        let mapped = this.interaction.mapInteraction(
 | 
						|
            extracted,
 | 
						|
            ['current', 'previous'],
 | 
						|
            this.mapPositionToPoint.bind(this)
 | 
						|
        )
 | 
						|
        for (let [target, interaction] of mapped.entries()) {
 | 
						|
            target.onMove(event, interaction)
 | 
						|
            interaction.updatePrevious()
 | 
						|
        }
 | 
						|
        this.interaction.updatePrevious()
 | 
						|
    }
 | 
						|
 | 
						|
    onEnd(event) {
 | 
						|
        let extracted = this.extractPoint(event, 'changedTouches')
 | 
						|
        this.endInteraction(event, extracted)
 | 
						|
        let mapped = this.interaction.mapInteraction(extracted, ['ended'], this.mapPositionToPoint.bind(this))
 | 
						|
        for (let [target, interaction] of mapped.entries()) {
 | 
						|
            target.onEnd(event, interaction)
 | 
						|
        }
 | 
						|
        this.finishInteraction(event, extracted)
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     *
 | 
						|
     *
 | 
						|
     * @static
 | 
						|
     * @param {string|array} types - An event type, an array of event types or event types seperated by a space sign. The following
 | 
						|
     *     events are possible:
 | 
						|
     *         pan, panstart, panmove, panend, pancancel, panleft, panright, panup, pandown
 | 
						|
     *         pinch, pinchstart, pinchmove, pinchend, pinchcancel, pinchin, pinchout
 | 
						|
     *         press, pressup
 | 
						|
     *         rotate, rotatestart, rotatemove, rotateend, rotatecancel
 | 
						|
     *         swipe, swipeleft, swiperight, swipeup, swipedown
 | 
						|
     *         tap
 | 
						|
     * @param {HTMLElement|HTMLElement[]} elements - An HTML element or an array of HTML elements.
 | 
						|
     * @param {function} [cb] - The callback. A function which is executed after the event occurs. Receives the event object as the
 | 
						|
     *     first paramter
 | 
						|
     * @param {object} [opts] - An options object. See the hammer documentation for more details.
 | 
						|
     */
 | 
						|
    static on(types, elements, cb, opts = {}) {
 | 
						|
        opts = Object.assign({}, {}, opts)
 | 
						|
 | 
						|
        if (typeof Hammer === 'undefined') {
 | 
						|
            console.error('Hammer.js not found!')
 | 
						|
            return this
 | 
						|
        }
 | 
						|
 | 
						|
        if (typeof Hammer.__hammers === 'undefined') {
 | 
						|
            Hammer.__hammers = new Map()
 | 
						|
        }
 | 
						|
 | 
						|
        // convert to array
 | 
						|
        types = Array.isArray(types) ? types : types.split(/\s/)
 | 
						|
        if (elements instanceof NodeList || elements instanceof HTMLCollection) {
 | 
						|
            elements = Array.from(elements)
 | 
						|
        }
 | 
						|
        elements = Array.isArray(elements) ? elements : [elements]
 | 
						|
 | 
						|
        for (let i = 0; i < types.length; i++) {
 | 
						|
            const type = types[i].toLowerCase()
 | 
						|
 | 
						|
            // list of hammer events
 | 
						|
            const useHammer = /^(tap|doubletap|press|pan|swipe|pinch|rotate).*$/.test(type)
 | 
						|
 | 
						|
            // if it is a hammer event
 | 
						|
            if (useHammer) {
 | 
						|
                for (let j = 0; j < elements.length; j++) {
 | 
						|
                    // if(elements[j].tagName == "svg") return false;
 | 
						|
 | 
						|
                    let hammer = new Hammer(elements[j], opts)
 | 
						|
 | 
						|
                    if (window.propagating !== 'undefined') {
 | 
						|
                        hammer = propagating(hammer)
 | 
						|
                    }
 | 
						|
 | 
						|
                    // recognizers
 | 
						|
                    if (type.startsWith('pan')) {
 | 
						|
                        hammer.get('pan').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts))
 | 
						|
                    } else if (type.startsWith('pinch')) {
 | 
						|
                        hammer.get('pinch').set(Object.assign({ enable: true }, opts))
 | 
						|
                    } else if (type.startsWith('press')) {
 | 
						|
                        hammer.get('press').set(opts)
 | 
						|
                    } else if (type.startsWith('rotate')) {
 | 
						|
                        hammer.get('rotate').set(Object.assign({ enable: true }, opts))
 | 
						|
                    } else if (type.startsWith('swipe')) {
 | 
						|
                        hammer.get('swipe').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts))
 | 
						|
                    } else if (type.startsWith('tap')) {
 | 
						|
                        hammer.get('tap').set(opts)
 | 
						|
                    }
 | 
						|
 | 
						|
                    hammer.on(type, event => {
 | 
						|
                        cb(event)
 | 
						|
                    })
 | 
						|
 | 
						|
                    if (Hammer.__hammers.has(elements[j])) {
 | 
						|
                        const elementHammers = Hammer.__hammers.get(elements[j])
 | 
						|
                        elementHammers.push(hammer)
 | 
						|
                        Hammer.__hammers.set(elements[j], elementHammers)
 | 
						|
                    } else {
 | 
						|
                        Hammer.__hammers.set(elements[j], [hammer])
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                for (let j = 0; j < elements.length; j++) {
 | 
						|
                    Hammer.on(elements[j], type, event => {
 | 
						|
                        cb(event)
 | 
						|
                    })
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     *
 | 
						|
     *
 | 
						|
     * @static
 | 
						|
     * @param {HTMLElement|HTMLElement[]} elements - An HTML element or an array of HTML elements.
 | 
						|
     */
 | 
						|
    static off(elements) {
 | 
						|
        if (typeof Hammer === 'undefined') {
 | 
						|
            console.error('Hammer.js not found!')
 | 
						|
            return this
 | 
						|
        }
 | 
						|
 | 
						|
        // convert to array
 | 
						|
        if (elements instanceof NodeList || elements instanceof HTMLCollection) {
 | 
						|
            elements = Array.from(elements)
 | 
						|
        }
 | 
						|
        elements = Array.isArray(elements) ? elements : [elements]
 | 
						|
 | 
						|
        for (let i = 0; i < elements.length; i++) {
 | 
						|
            const element = elements[i]
 | 
						|
 | 
						|
            if (Hammer.__hammers.has(element)) {
 | 
						|
                const elementHammers = Hammer.__hammers.get(element)
 | 
						|
                elementHammers.forEach(it => it.destroy())
 | 
						|
                Hammer.__hammers.delete(element)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
window.InteractionMapper = InteractionMapper
 |