Merge branch 'main' of gitea.iwm-tuebingen.de:IWMBrowser/iwmlib into main
* 'main' of gitea.iwm-tuebingen.de:IWMBrowser/iwmlib: Removed console.logs Fixed timestamp name mismatch. Updated iwmlib and removed log statement. Added pluggable throw behavior. Replaced requestAnimationFrame with setInterval Moved throws to a single loop. Added cancelAnimationFrame update code block to reflect current state Fixed html double code Fixed problem with scrolling text in card drawers. added fix for viewBox rotation bug fixed rollup documented/ solved svg rotation bug # Conflicts: # package.json
This commit is contained in:
		
						commit
						57d6e8b461
					
				
							
								
								
									
										0
									
								
								bin/browser.sh
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								bin/browser.sh
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
								
								
									
										1258
									
								
								browser/menu.js
									
									
									
									
									
								
							
							
						
						
									
										1258
									
								
								browser/menu.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										196
									
								
								dist/iwmlib.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										196
									
								
								dist/iwmlib.js
									
									
									
									
										vendored
									
									
								
							@ -3213,6 +3213,40 @@
 | 
			
		||||
     *
 | 
			
		||||
     * @constructor
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class RequestFrameThrower {
 | 
			
		||||
        /** Implemenents the standard throw behavior. */
 | 
			
		||||
        animateThrow(throwable) {
 | 
			
		||||
            /*** Calls the animateThrow method in sync with the display link. */
 | 
			
		||||
            requestAnimationFrame(throwable.animateThrow.bind(throwable));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class TimeoutThrower {
 | 
			
		||||
 | 
			
		||||
        constructor(delay=20) {
 | 
			
		||||
            this.throwables = new Set();
 | 
			
		||||
            this.delay = delay;
 | 
			
		||||
            setTimeout(this.animateStep.bind(this), this.delay);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        animateThrow(throwable) {
 | 
			
		||||
            this.throwables.add(throwable);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        animateStep() {
 | 
			
		||||
            let active = [...this.throwables];
 | 
			
		||||
            this.throwables.clear();
 | 
			
		||||
            for(let throwable of active) {
 | 
			
		||||
                throwable.animateThrow();
 | 
			
		||||
            }
 | 
			
		||||
            setTimeout(this.animateStep.bind(this), this.delay);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let thrower = new RequestFrameThrower();
 | 
			
		||||
 | 
			
		||||
    class Throwable {
 | 
			
		||||
        constructor({
 | 
			
		||||
            movableX = true,
 | 
			
		||||
@ -3220,7 +3254,7 @@
 | 
			
		||||
            throwVisibility = 44,
 | 
			
		||||
            throwDamping = 0.95,
 | 
			
		||||
            autoThrow = true,
 | 
			
		||||
            onThrowFinished = null,
 | 
			
		||||
            onThrowFinished = null
 | 
			
		||||
        } = {}) {
 | 
			
		||||
            this.movableX = movableX;
 | 
			
		||||
            this.movableY = movableY;
 | 
			
		||||
@ -3229,9 +3263,16 @@
 | 
			
		||||
            this.autoThrow = autoThrow;
 | 
			
		||||
            this.velocities = [];
 | 
			
		||||
            this.velocity = null;
 | 
			
		||||
            this.timestamp = null;
 | 
			
		||||
            this.lastframe = null;
 | 
			
		||||
            this.onThrowFinished = onThrowFinished;
 | 
			
		||||
            //console.log("onThrowFinished", onThrowFinished)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static defaultThrow() {
 | 
			
		||||
            thrower = new RequestFrameThrower();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static timeoutThrow() {
 | 
			
		||||
            thrower = new TimeoutThrower();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        observeVelocity() {
 | 
			
		||||
@ -3253,7 +3294,7 @@
 | 
			
		||||
                    t: t,
 | 
			
		||||
                    dt: dt,
 | 
			
		||||
                    dx: delta.x / number,
 | 
			
		||||
                    dy: delta.y / number,
 | 
			
		||||
                    dy: delta.y / number
 | 
			
		||||
                };
 | 
			
		||||
                this.velocities.push(velocity);
 | 
			
		||||
                while (this.velocities.length > buffer) {
 | 
			
		||||
@ -3262,8 +3303,23 @@
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        addTestVelocity(delta, dt=20, buffer = 5) {
 | 
			
		||||
            let t = performance.now();
 | 
			
		||||
            this.lastframe = t;
 | 
			
		||||
            let velocity = {
 | 
			
		||||
                t: t,
 | 
			
		||||
                dt: dt,
 | 
			
		||||
                dx: delta.x ,
 | 
			
		||||
                dy: delta.y 
 | 
			
		||||
            };
 | 
			
		||||
            for(let i=0; i<buffer; i++) {
 | 
			
		||||
                velocity.t += dt;
 | 
			
		||||
                this.velocities.push(velocity);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        meanVelocity(milliseconds = 30) {
 | 
			
		||||
            this.addVelocity({ x: 0, y: 0, number: 1 });
 | 
			
		||||
         //   this.addVelocity({ x: 0, y: 0, number: 1 })
 | 
			
		||||
            let sum = { x: 0, y: 0 };
 | 
			
		||||
            let count = 0;
 | 
			
		||||
            let t = 0;
 | 
			
		||||
@ -3284,6 +3340,7 @@
 | 
			
		||||
        killAnimation() {
 | 
			
		||||
            this.velocity = null;
 | 
			
		||||
            this.velocities = [];
 | 
			
		||||
            this.lastframe = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        startThrow() {
 | 
			
		||||
@ -3305,6 +3362,10 @@
 | 
			
		||||
            return dt
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        recurseAnimateThrow() {
 | 
			
		||||
            ThrowableObjects.add(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        animateThrow(time) {
 | 
			
		||||
            if (this.velocity != null) {
 | 
			
		||||
                let dt = this._throwDeltaTime();
 | 
			
		||||
@ -3315,7 +3376,7 @@
 | 
			
		||||
                if (nextLength > prevLength) {
 | 
			
		||||
                    let factor = nextLength / prevLength;
 | 
			
		||||
                    next = Points$1.multiplyScalar(next, 1 / factor);
 | 
			
		||||
                    console.log('Prevent acceleration', factor, this.velocity, next);
 | 
			
		||||
                    // console.log('Prevent acceleration', factor, this.velocity, next)
 | 
			
		||||
                }
 | 
			
		||||
                this.velocity = next;
 | 
			
		||||
                let d = Points$1.multiplyScalar(this.velocity, dt);
 | 
			
		||||
@ -3323,11 +3384,11 @@
 | 
			
		||||
 | 
			
		||||
                this.onDragUpdate(d);
 | 
			
		||||
                if (dt == 0 || this.needsAnimation()) {
 | 
			
		||||
                    requestAnimationFrame(this.animateThrow.bind(this));
 | 
			
		||||
                    thrower.animateThrow(this);
 | 
			
		||||
                    return
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (this.isOutside()) {
 | 
			
		||||
                        requestAnimationFrame(this.animateThrow.bind(this));
 | 
			
		||||
                        thrower.animateThrow(this);
 | 
			
		||||
                        return
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@ -3351,7 +3412,7 @@
 | 
			
		||||
            let next = Points$1.multiplyScalar(velocity, this.throwDamping);
 | 
			
		||||
            return {
 | 
			
		||||
                x: this.movableX ? next.x : 0,
 | 
			
		||||
                y: this.movableY ? next.y : 0,
 | 
			
		||||
                y: this.movableY ? next.y : 0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -3396,7 +3457,7 @@
 | 
			
		||||
            scaleCloseBuffer = 0.05,
 | 
			
		||||
            maxRotation = Angle.degree2radian(5),
 | 
			
		||||
            minInteractionDistance = 0,
 | 
			
		||||
            useLowPassFilter = false,
 | 
			
		||||
            useLowPassFilter = false
 | 
			
		||||
        } = {}) {
 | 
			
		||||
            if (rotationDegrees != null && rotation != null) {
 | 
			
		||||
                throw new Error('Use rotationDegrees or rotation but not both')
 | 
			
		||||
@ -3411,7 +3472,7 @@
 | 
			
		||||
                throwVisibility,
 | 
			
		||||
                throwDamping,
 | 
			
		||||
                autoThrow,
 | 
			
		||||
                onThrowFinished,
 | 
			
		||||
                onThrowFinished
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
@ -3486,7 +3547,7 @@
 | 
			
		||||
 | 
			
		||||
        _callCloseCallbacks() {
 | 
			
		||||
            if (this.onClose) {
 | 
			
		||||
                this.onClose.forEach((callback) => callback(this));
 | 
			
		||||
                this.onClose.forEach(callback => callback(this));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -3506,6 +3567,9 @@
 | 
			
		||||
            let delta = interaction.delta();
 | 
			
		||||
 | 
			
		||||
            if (delta != null) {
 | 
			
		||||
                /* uo: Is this the best place to add velocity? It works with scrollable text in card drawers but
 | 
			
		||||
                has to be tested */
 | 
			
		||||
                this.addVelocity(delta);
 | 
			
		||||
                let rotate = delta.rotate;
 | 
			
		||||
                let zoom = delta.zoom;
 | 
			
		||||
                if (this.maxRotation != null) {
 | 
			
		||||
@ -3524,9 +3588,10 @@
 | 
			
		||||
                    zoomDelta *= ratio;
 | 
			
		||||
                    zoom = 1 + zoomDelta;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.transform(delta, zoom, rotate, delta.about);
 | 
			
		||||
                this.addVelocity(delta); // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
 | 
			
		||||
                /* uo: This is too late an dangerous. transform sometimes modifies delta
 | 
			
		||||
                this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
 | 
			
		||||
                */
 | 
			
		||||
 | 
			
		||||
                if (zoom != 1) this.interactionAnchor = delta.about;
 | 
			
		||||
            }
 | 
			
		||||
@ -3637,11 +3702,11 @@
 | 
			
		||||
                if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) {
 | 
			
		||||
                    this.zoom(this.minScale, {
 | 
			
		||||
                        animate: 0.2,
 | 
			
		||||
                        onComplete: this.close.bind(this),
 | 
			
		||||
                        onComplete: this.close.bind(this)
 | 
			
		||||
                    });
 | 
			
		||||
                } else if (this.scale < this.minScale + this.scaleCloseThreshold) {
 | 
			
		||||
                    this.zoom(this.minScale + this.scaleCloseThreshold, {
 | 
			
		||||
                        animate: 0.4,
 | 
			
		||||
                        animate: 0.4
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
@ -3663,12 +3728,12 @@
 | 
			
		||||
                        x: '+=' + d.x,
 | 
			
		||||
                        y: '+=' + d.y,
 | 
			
		||||
                        /* scale: scale, uo: not defined, why was this here? */
 | 
			
		||||
                        onUpdate: (e) => {
 | 
			
		||||
                        onUpdate: e => {
 | 
			
		||||
                            let p = this.position;
 | 
			
		||||
                            let dx = p.x - startPos.x;
 | 
			
		||||
                            let dy = p.x - startPos.y;
 | 
			
		||||
                            this.onMoved(dx, dy);
 | 
			
		||||
                        },
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._move(d);
 | 
			
		||||
@ -3697,7 +3762,7 @@
 | 
			
		||||
                        scale: scale,
 | 
			
		||||
                        delay: delay,
 | 
			
		||||
                        onComplete: onComplete,
 | 
			
		||||
                        onUpdate: this.onZoomed.bind(this),
 | 
			
		||||
                        onUpdate: this.onZoomed.bind(this)
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.scale = scale;
 | 
			
		||||
@ -3715,7 +3780,7 @@
 | 
			
		||||
        transform(translate, zoom, rotate, anchor) {
 | 
			
		||||
            let delta = {
 | 
			
		||||
                x: this.movableX ? translate.x : 0,
 | 
			
		||||
                y: this.movableY ? translate.y : 0,
 | 
			
		||||
                y: this.movableY ? translate.y : 0
 | 
			
		||||
            };
 | 
			
		||||
            if (this.resizable) var vzoom = zoom;
 | 
			
		||||
            if (!this.translatable) delta = { x: 0, y: 0 };
 | 
			
		||||
@ -3730,9 +3795,9 @@
 | 
			
		||||
                        rotate: 0,
 | 
			
		||||
                        about: anchor,
 | 
			
		||||
                        fast: false,
 | 
			
		||||
                        type: UPDATE,
 | 
			
		||||
                        type: UPDATE
 | 
			
		||||
                    });
 | 
			
		||||
                    this.onTransform.forEach(function (f) {
 | 
			
		||||
                    this.onTransform.forEach(function(f) {
 | 
			
		||||
                        f(event);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
@ -3761,9 +3826,9 @@
 | 
			
		||||
                    translate: delta,
 | 
			
		||||
                    scale: newScale,
 | 
			
		||||
                    rotate: rotate,
 | 
			
		||||
                    about: anchor,
 | 
			
		||||
                    about: anchor
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -3829,7 +3894,7 @@
 | 
			
		||||
                if (this.scale > this.maxScale) zoom = 1 - amount;
 | 
			
		||||
                if (zoom != 1) {
 | 
			
		||||
                    this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor);
 | 
			
		||||
                    requestAnimationFrame((dt) => {
 | 
			
		||||
                    requestAnimationFrame(dt => {
 | 
			
		||||
                        this.animateZoomBounce(dt);
 | 
			
		||||
                    });
 | 
			
		||||
                    return
 | 
			
		||||
@ -3886,9 +3951,9 @@
 | 
			
		||||
                    rotate: 0,
 | 
			
		||||
                    about: null,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: START,
 | 
			
		||||
                    type: START
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -3904,13 +3969,13 @@
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        onEnd(event, interaction) {
 | 
			
		||||
            console.log('Scatter.onEnd', this.dragging);
 | 
			
		||||
            //console.log('Scatter.onEnd', this.dragging)
 | 
			
		||||
            if (interaction.isFinished()) {
 | 
			
		||||
                this.endGesture(interaction);
 | 
			
		||||
                this.dragging = false;
 | 
			
		||||
                for (let key of interaction.ended.keys()) {
 | 
			
		||||
                    if (interaction.isTap(key)) {
 | 
			
		||||
                        console.log('Scatter.isTap');
 | 
			
		||||
                        //console.log('Scatter.isTap')
 | 
			
		||||
                        let point = interaction.ended.get(key);
 | 
			
		||||
                        this.onTap(event, interaction, point);
 | 
			
		||||
                    }
 | 
			
		||||
@ -3922,9 +3987,9 @@
 | 
			
		||||
                        rotate: 0,
 | 
			
		||||
                        about: null,
 | 
			
		||||
                        fast: false,
 | 
			
		||||
                        type: END,
 | 
			
		||||
                        type: END
 | 
			
		||||
                    });
 | 
			
		||||
                    this.onTransform.forEach(function (f) {
 | 
			
		||||
                    this.onTransform.forEach(function(f) {
 | 
			
		||||
                        f(event);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
@ -3938,7 +4003,7 @@
 | 
			
		||||
        //onTap(event, interaction, point) {}
 | 
			
		||||
 | 
			
		||||
        onTap(event, interaction, point) {
 | 
			
		||||
            console.log('AbstractScatter.onTap', this.tapDelegate, interaction);
 | 
			
		||||
            //console.log('AbstractScatter.onTap', this.tapDelegate, interaction)
 | 
			
		||||
            if (this.tapDelegate) {
 | 
			
		||||
                Events.stop(event);
 | 
			
		||||
                this.tapDelegate.tap(event, 'scatter');
 | 
			
		||||
@ -3952,9 +4017,9 @@
 | 
			
		||||
                    translate: delta,
 | 
			
		||||
                    scale: this.scale,
 | 
			
		||||
                    about: this.currentAbout,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -3966,9 +4031,9 @@
 | 
			
		||||
                    scale: this.scale,
 | 
			
		||||
                    about: this.currentAbout,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -3980,9 +4045,9 @@
 | 
			
		||||
                    translate: { x: dx, y: dy },
 | 
			
		||||
                    about: about,
 | 
			
		||||
                    fast: true,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -3993,9 +4058,9 @@
 | 
			
		||||
                let event = new ScatterEvent(this, {
 | 
			
		||||
                    scale: this.scale,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -4009,9 +4074,9 @@
 | 
			
		||||
                    scale: this.scale,
 | 
			
		||||
                    about: about,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -4043,7 +4108,7 @@
 | 
			
		||||
                useCapture = true,
 | 
			
		||||
                capturePointerEvents = true,
 | 
			
		||||
                touchAction = 'none',
 | 
			
		||||
                debugCanvas = null,
 | 
			
		||||
                debugCanvas = null
 | 
			
		||||
            } = {}
 | 
			
		||||
        ) {
 | 
			
		||||
            this.onCapture = null;
 | 
			
		||||
@ -4055,7 +4120,7 @@
 | 
			
		||||
                movement of scatter objects, the touchmove event has to be bound again.
 | 
			
		||||
                */
 | 
			
		||||
                if (Capabilities.isSafari) {
 | 
			
		||||
                    document.addEventListener('touchmove', (event) => this.preventPinch(event), false);
 | 
			
		||||
                    document.addEventListener('touchmove', event => this.preventPinch(event), false);
 | 
			
		||||
                    stopEvents = false;
 | 
			
		||||
                } else {
 | 
			
		||||
                    stopEvents = true;
 | 
			
		||||
@ -4070,11 +4135,11 @@
 | 
			
		||||
            this.delegate = new InteractionMapper$1(element, this, {
 | 
			
		||||
                useCapture,
 | 
			
		||||
                capturePointerEvents,
 | 
			
		||||
                mouseWheelElement: window,
 | 
			
		||||
                mouseWheelElement: window
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (debugCanvas !== null) {
 | 
			
		||||
                requestAnimationFrame((dt) => {
 | 
			
		||||
                requestAnimationFrame(dt => {
 | 
			
		||||
                    this.showTouches(dt, debugCanvas);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -4096,7 +4161,7 @@
 | 
			
		||||
                context.fill();
 | 
			
		||||
                context.stroke();
 | 
			
		||||
            }
 | 
			
		||||
            requestAnimationFrame((dt) => {
 | 
			
		||||
            requestAnimationFrame(dt => {
 | 
			
		||||
                this.showTouches(dt, canvas);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
@ -4238,7 +4303,7 @@
 | 
			
		||||
                scaleCloseBuffer = 0.05,
 | 
			
		||||
                useLowPassFilter = false,
 | 
			
		||||
                maxRotation = Angle.degree2radian(15),
 | 
			
		||||
                minInteractionDistance = 200,
 | 
			
		||||
                minInteractionDistance = 200
 | 
			
		||||
            } = {}
 | 
			
		||||
        ) {
 | 
			
		||||
            super({
 | 
			
		||||
@ -4265,7 +4330,7 @@
 | 
			
		||||
                onClose,
 | 
			
		||||
                useLowPassFilter,
 | 
			
		||||
                maxRotation,
 | 
			
		||||
                minInteractionDistance,
 | 
			
		||||
                minInteractionDistance
 | 
			
		||||
            });
 | 
			
		||||
            if (container == null || width == null || height == null) {
 | 
			
		||||
                throw new Error('Invalid value: null')
 | 
			
		||||
@ -4293,7 +4358,7 @@
 | 
			
		||||
                height: height,
 | 
			
		||||
                scale: startScale,
 | 
			
		||||
                rotation: this.startRotationDegrees,
 | 
			
		||||
                transformOrigin: transformOrigin,
 | 
			
		||||
                transformOrigin: transformOrigin
 | 
			
		||||
            };
 | 
			
		||||
            this.tapNodes = new Map();
 | 
			
		||||
 | 
			
		||||
@ -4315,15 +4380,15 @@
 | 
			
		||||
                button.className = 'interactiveElement';
 | 
			
		||||
                this.element.appendChild(button);
 | 
			
		||||
 | 
			
		||||
                button.addEventListener('pointerdown', (e) => {
 | 
			
		||||
                button.addEventListener('pointerdown', e => {
 | 
			
		||||
                    this.startResize(e);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                button.addEventListener('pointermove', (e) => {
 | 
			
		||||
                button.addEventListener('pointermove', e => {
 | 
			
		||||
                    this.resize(e);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                button.addEventListener('pointerup', (e) => {
 | 
			
		||||
                button.addEventListener('pointerup', e => {
 | 
			
		||||
                    this.stopResize(e);
 | 
			
		||||
                });
 | 
			
		||||
                this.resizeButton = button;
 | 
			
		||||
@ -4340,7 +4405,7 @@
 | 
			
		||||
                scale: this.scale,
 | 
			
		||||
                x: this.x,
 | 
			
		||||
                y: this.y,
 | 
			
		||||
                rotation: this.rotation,
 | 
			
		||||
                rotation: this.rotation
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -4391,7 +4456,7 @@
 | 
			
		||||
                top: rect.top - stage.top,
 | 
			
		||||
                left: rect.left - stage.left,
 | 
			
		||||
                width: rect.width,
 | 
			
		||||
                height: rect.height,
 | 
			
		||||
                height: rect.height
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -4432,7 +4497,7 @@
 | 
			
		||||
        set scale(scale) {
 | 
			
		||||
            TweenLite.set(this.element, {
 | 
			
		||||
                scale: scale,
 | 
			
		||||
                transformOrigin: this.transformOrigin,
 | 
			
		||||
                transformOrigin: this.transformOrigin
 | 
			
		||||
            });
 | 
			
		||||
            this._scale = scale;
 | 
			
		||||
        }
 | 
			
		||||
@ -4464,9 +4529,9 @@
 | 
			
		||||
        hide() {
 | 
			
		||||
            TweenLite.to(this.element, 0.1, {
 | 
			
		||||
                display: 'none',
 | 
			
		||||
                onComplete: (e) => {
 | 
			
		||||
                onComplete: e => {
 | 
			
		||||
                    this.element.parentNode.removeChild(this.element);
 | 
			
		||||
                },
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -4480,7 +4545,7 @@
 | 
			
		||||
                x: p.x,
 | 
			
		||||
                y: p.y,
 | 
			
		||||
                rotation: rotationDegrees,
 | 
			
		||||
                transformOrigin: this.transformOrigin,
 | 
			
		||||
                transformOrigin: this.transformOrigin
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -4541,7 +4606,7 @@
 | 
			
		||||
 | 
			
		||||
            let oldPostition = {
 | 
			
		||||
                x: this.element.getBoundingClientRect().left,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top
 | 
			
		||||
            };
 | 
			
		||||
            this.bringToFront();
 | 
			
		||||
 | 
			
		||||
@ -4549,7 +4614,7 @@
 | 
			
		||||
 | 
			
		||||
            let newPostition = {
 | 
			
		||||
                x: this.element.getBoundingClientRect().left,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            let offset = Points$1.subtract(oldPostition, newPostition);
 | 
			
		||||
@ -4594,7 +4659,7 @@
 | 
			
		||||
                )
 | 
			
		||||
                    TweenLite.to(this.element, 0, {
 | 
			
		||||
                        width: this.element.offsetWidth + resizeW / this.scale,
 | 
			
		||||
                        height: this.element.offsetHeight + resizeH / this.scale,
 | 
			
		||||
                        height: this.element.offsetHeight + resizeH / this.scale
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                this.oldX = e.clientX;
 | 
			
		||||
@ -4611,12 +4676,12 @@
 | 
			
		||||
            let event = new CustomEvent('resizeEnded');
 | 
			
		||||
            let oldPostition = {
 | 
			
		||||
                x: this.element.getBoundingClientRect().left,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top
 | 
			
		||||
            };
 | 
			
		||||
            this.element.style.transformOrigin = '50% 50%';
 | 
			
		||||
            let newPostition = {
 | 
			
		||||
                x: this.element.getBoundingClientRect().left,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top
 | 
			
		||||
            };
 | 
			
		||||
            let offset = Points$1.subtract(oldPostition, newPostition);
 | 
			
		||||
 | 
			
		||||
@ -4789,7 +4854,6 @@
 | 
			
		||||
        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)
 | 
			
		||||
@ -8195,7 +8259,6 @@
 | 
			
		||||
                    image,
 | 
			
		||||
                });
 | 
			
		||||
                let center = this._calculateCenterRelativeTo(target, image);
 | 
			
		||||
                console.log('_calculateCenterRelativeTo', center);
 | 
			
		||||
                TweenLite.set(maskImage, {
 | 
			
		||||
                    transformOrigin: `${center.x} ${center.y}`,
 | 
			
		||||
                });
 | 
			
		||||
@ -9099,7 +9162,6 @@
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static _calculateCenterRelativeTo(target, image) {
 | 
			
		||||
            // console.log('_calculateCenterRelativeTo', target, image)
 | 
			
		||||
            let bbox = image.getBBox();
 | 
			
		||||
            let width = bbox.width;
 | 
			
		||||
            let height = bbox.height;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										184
									
								
								dist/iwmlib.pixi.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										184
									
								
								dist/iwmlib.pixi.js
									
									
									
									
										vendored
									
									
								
							@ -6778,6 +6778,40 @@
 | 
			
		||||
     *
 | 
			
		||||
     * @constructor
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class RequestFrameThrower {
 | 
			
		||||
        /** Implemenents the standard throw behavior. */
 | 
			
		||||
        animateThrow(throwable) {
 | 
			
		||||
            /*** Calls the animateThrow method in sync with the display link. */
 | 
			
		||||
            requestAnimationFrame(throwable.animateThrow.bind(throwable));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class TimeoutThrower {
 | 
			
		||||
 | 
			
		||||
        constructor(delay=20) {
 | 
			
		||||
            this.throwables = new Set();
 | 
			
		||||
            this.delay = delay;
 | 
			
		||||
            setTimeout(this.animateStep.bind(this), this.delay);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        animateThrow(throwable) {
 | 
			
		||||
            this.throwables.add(throwable);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        animateStep() {
 | 
			
		||||
            let active = [...this.throwables];
 | 
			
		||||
            this.throwables.clear();
 | 
			
		||||
            for(let throwable of active) {
 | 
			
		||||
                throwable.animateThrow();
 | 
			
		||||
            }
 | 
			
		||||
            setTimeout(this.animateStep.bind(this), this.delay);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let thrower = new RequestFrameThrower();
 | 
			
		||||
 | 
			
		||||
    class Throwable {
 | 
			
		||||
        constructor({
 | 
			
		||||
            movableX = true,
 | 
			
		||||
@ -6785,7 +6819,7 @@
 | 
			
		||||
            throwVisibility = 44,
 | 
			
		||||
            throwDamping = 0.95,
 | 
			
		||||
            autoThrow = true,
 | 
			
		||||
            onThrowFinished = null,
 | 
			
		||||
            onThrowFinished = null
 | 
			
		||||
        } = {}) {
 | 
			
		||||
            this.movableX = movableX;
 | 
			
		||||
            this.movableY = movableY;
 | 
			
		||||
@ -6794,9 +6828,16 @@
 | 
			
		||||
            this.autoThrow = autoThrow;
 | 
			
		||||
            this.velocities = [];
 | 
			
		||||
            this.velocity = null;
 | 
			
		||||
            this.timestamp = null;
 | 
			
		||||
            this.lastframe = null;
 | 
			
		||||
            this.onThrowFinished = onThrowFinished;
 | 
			
		||||
            //console.log("onThrowFinished", onThrowFinished)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static defaultThrow() {
 | 
			
		||||
            thrower = new RequestFrameThrower();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static timeoutThrow() {
 | 
			
		||||
            thrower = new TimeoutThrower();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        observeVelocity() {
 | 
			
		||||
@ -6818,7 +6859,7 @@
 | 
			
		||||
                    t: t,
 | 
			
		||||
                    dt: dt,
 | 
			
		||||
                    dx: delta.x / number,
 | 
			
		||||
                    dy: delta.y / number,
 | 
			
		||||
                    dy: delta.y / number
 | 
			
		||||
                };
 | 
			
		||||
                this.velocities.push(velocity);
 | 
			
		||||
                while (this.velocities.length > buffer) {
 | 
			
		||||
@ -6827,8 +6868,23 @@
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        addTestVelocity(delta, dt=20, buffer = 5) {
 | 
			
		||||
            let t = performance.now();
 | 
			
		||||
            this.lastframe = t;
 | 
			
		||||
            let velocity = {
 | 
			
		||||
                t: t,
 | 
			
		||||
                dt: dt,
 | 
			
		||||
                dx: delta.x ,
 | 
			
		||||
                dy: delta.y 
 | 
			
		||||
            };
 | 
			
		||||
            for(let i=0; i<buffer; i++) {
 | 
			
		||||
                velocity.t += dt;
 | 
			
		||||
                this.velocities.push(velocity);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        meanVelocity(milliseconds = 30) {
 | 
			
		||||
            this.addVelocity({ x: 0, y: 0, number: 1 });
 | 
			
		||||
         //   this.addVelocity({ x: 0, y: 0, number: 1 })
 | 
			
		||||
            let sum = { x: 0, y: 0 };
 | 
			
		||||
            let count = 0;
 | 
			
		||||
            let t = 0;
 | 
			
		||||
@ -6849,6 +6905,7 @@
 | 
			
		||||
        killAnimation() {
 | 
			
		||||
            this.velocity = null;
 | 
			
		||||
            this.velocities = [];
 | 
			
		||||
            this.lastframe = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        startThrow() {
 | 
			
		||||
@ -6870,6 +6927,10 @@
 | 
			
		||||
            return dt
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        recurseAnimateThrow() {
 | 
			
		||||
            ThrowableObjects.add(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        animateThrow(time) {
 | 
			
		||||
            if (this.velocity != null) {
 | 
			
		||||
                let dt = this._throwDeltaTime();
 | 
			
		||||
@ -6880,7 +6941,7 @@
 | 
			
		||||
                if (nextLength > prevLength) {
 | 
			
		||||
                    let factor = nextLength / prevLength;
 | 
			
		||||
                    next = Points.multiplyScalar(next, 1 / factor);
 | 
			
		||||
                    console.log('Prevent acceleration', factor, this.velocity, next);
 | 
			
		||||
                    // console.log('Prevent acceleration', factor, this.velocity, next)
 | 
			
		||||
                }
 | 
			
		||||
                this.velocity = next;
 | 
			
		||||
                let d = Points.multiplyScalar(this.velocity, dt);
 | 
			
		||||
@ -6888,11 +6949,11 @@
 | 
			
		||||
 | 
			
		||||
                this.onDragUpdate(d);
 | 
			
		||||
                if (dt == 0 || this.needsAnimation()) {
 | 
			
		||||
                    requestAnimationFrame(this.animateThrow.bind(this));
 | 
			
		||||
                    thrower.animateThrow(this);
 | 
			
		||||
                    return
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (this.isOutside()) {
 | 
			
		||||
                        requestAnimationFrame(this.animateThrow.bind(this));
 | 
			
		||||
                        thrower.animateThrow(this);
 | 
			
		||||
                        return
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@ -6916,7 +6977,7 @@
 | 
			
		||||
            let next = Points.multiplyScalar(velocity, this.throwDamping);
 | 
			
		||||
            return {
 | 
			
		||||
                x: this.movableX ? next.x : 0,
 | 
			
		||||
                y: this.movableY ? next.y : 0,
 | 
			
		||||
                y: this.movableY ? next.y : 0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -6961,7 +7022,7 @@
 | 
			
		||||
            scaleCloseBuffer = 0.05,
 | 
			
		||||
            maxRotation = Angle.degree2radian(5),
 | 
			
		||||
            minInteractionDistance = 0,
 | 
			
		||||
            useLowPassFilter = false,
 | 
			
		||||
            useLowPassFilter = false
 | 
			
		||||
        } = {}) {
 | 
			
		||||
            if (rotationDegrees != null && rotation != null) {
 | 
			
		||||
                throw new Error('Use rotationDegrees or rotation but not both')
 | 
			
		||||
@ -6976,7 +7037,7 @@
 | 
			
		||||
                throwVisibility,
 | 
			
		||||
                throwDamping,
 | 
			
		||||
                autoThrow,
 | 
			
		||||
                onThrowFinished,
 | 
			
		||||
                onThrowFinished
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
@ -7051,7 +7112,7 @@
 | 
			
		||||
 | 
			
		||||
        _callCloseCallbacks() {
 | 
			
		||||
            if (this.onClose) {
 | 
			
		||||
                this.onClose.forEach((callback) => callback(this));
 | 
			
		||||
                this.onClose.forEach(callback => callback(this));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -7071,6 +7132,9 @@
 | 
			
		||||
            let delta = interaction.delta();
 | 
			
		||||
 | 
			
		||||
            if (delta != null) {
 | 
			
		||||
                /* uo: Is this the best place to add velocity? It works with scrollable text in card drawers but
 | 
			
		||||
                has to be tested */
 | 
			
		||||
                this.addVelocity(delta);
 | 
			
		||||
                let rotate = delta.rotate;
 | 
			
		||||
                let zoom = delta.zoom;
 | 
			
		||||
                if (this.maxRotation != null) {
 | 
			
		||||
@ -7089,9 +7153,10 @@
 | 
			
		||||
                    zoomDelta *= ratio;
 | 
			
		||||
                    zoom = 1 + zoomDelta;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.transform(delta, zoom, rotate, delta.about);
 | 
			
		||||
                this.addVelocity(delta); // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
 | 
			
		||||
                /* uo: This is too late an dangerous. transform sometimes modifies delta
 | 
			
		||||
                this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
 | 
			
		||||
                */
 | 
			
		||||
 | 
			
		||||
                if (zoom != 1) this.interactionAnchor = delta.about;
 | 
			
		||||
            }
 | 
			
		||||
@ -7202,11 +7267,11 @@
 | 
			
		||||
                if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) {
 | 
			
		||||
                    this.zoom(this.minScale, {
 | 
			
		||||
                        animate: 0.2,
 | 
			
		||||
                        onComplete: this.close.bind(this),
 | 
			
		||||
                        onComplete: this.close.bind(this)
 | 
			
		||||
                    });
 | 
			
		||||
                } else if (this.scale < this.minScale + this.scaleCloseThreshold) {
 | 
			
		||||
                    this.zoom(this.minScale + this.scaleCloseThreshold, {
 | 
			
		||||
                        animate: 0.4,
 | 
			
		||||
                        animate: 0.4
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
@ -7228,12 +7293,12 @@
 | 
			
		||||
                        x: '+=' + d.x,
 | 
			
		||||
                        y: '+=' + d.y,
 | 
			
		||||
                        /* scale: scale, uo: not defined, why was this here? */
 | 
			
		||||
                        onUpdate: (e) => {
 | 
			
		||||
                        onUpdate: e => {
 | 
			
		||||
                            let p = this.position;
 | 
			
		||||
                            let dx = p.x - startPos.x;
 | 
			
		||||
                            let dy = p.x - startPos.y;
 | 
			
		||||
                            this.onMoved(dx, dy);
 | 
			
		||||
                        },
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._move(d);
 | 
			
		||||
@ -7262,7 +7327,7 @@
 | 
			
		||||
                        scale: scale,
 | 
			
		||||
                        delay: delay,
 | 
			
		||||
                        onComplete: onComplete,
 | 
			
		||||
                        onUpdate: this.onZoomed.bind(this),
 | 
			
		||||
                        onUpdate: this.onZoomed.bind(this)
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.scale = scale;
 | 
			
		||||
@ -7280,7 +7345,7 @@
 | 
			
		||||
        transform(translate, zoom, rotate, anchor) {
 | 
			
		||||
            let delta = {
 | 
			
		||||
                x: this.movableX ? translate.x : 0,
 | 
			
		||||
                y: this.movableY ? translate.y : 0,
 | 
			
		||||
                y: this.movableY ? translate.y : 0
 | 
			
		||||
            };
 | 
			
		||||
            if (this.resizable) var vzoom = zoom;
 | 
			
		||||
            if (!this.translatable) delta = { x: 0, y: 0 };
 | 
			
		||||
@ -7295,9 +7360,9 @@
 | 
			
		||||
                        rotate: 0,
 | 
			
		||||
                        about: anchor,
 | 
			
		||||
                        fast: false,
 | 
			
		||||
                        type: UPDATE,
 | 
			
		||||
                        type: UPDATE
 | 
			
		||||
                    });
 | 
			
		||||
                    this.onTransform.forEach(function (f) {
 | 
			
		||||
                    this.onTransform.forEach(function(f) {
 | 
			
		||||
                        f(event);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
@ -7326,9 +7391,9 @@
 | 
			
		||||
                    translate: delta,
 | 
			
		||||
                    scale: newScale,
 | 
			
		||||
                    rotate: rotate,
 | 
			
		||||
                    about: anchor,
 | 
			
		||||
                    about: anchor
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -7394,7 +7459,7 @@
 | 
			
		||||
                if (this.scale > this.maxScale) zoom = 1 - amount;
 | 
			
		||||
                if (zoom != 1) {
 | 
			
		||||
                    this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor);
 | 
			
		||||
                    requestAnimationFrame((dt) => {
 | 
			
		||||
                    requestAnimationFrame(dt => {
 | 
			
		||||
                        this.animateZoomBounce(dt);
 | 
			
		||||
                    });
 | 
			
		||||
                    return
 | 
			
		||||
@ -7451,9 +7516,9 @@
 | 
			
		||||
                    rotate: 0,
 | 
			
		||||
                    about: null,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: START,
 | 
			
		||||
                    type: START
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -7469,13 +7534,13 @@
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        onEnd(event, interaction) {
 | 
			
		||||
            console.log('Scatter.onEnd', this.dragging);
 | 
			
		||||
            //console.log('Scatter.onEnd', this.dragging)
 | 
			
		||||
            if (interaction.isFinished()) {
 | 
			
		||||
                this.endGesture(interaction);
 | 
			
		||||
                this.dragging = false;
 | 
			
		||||
                for (let key of interaction.ended.keys()) {
 | 
			
		||||
                    if (interaction.isTap(key)) {
 | 
			
		||||
                        console.log('Scatter.isTap');
 | 
			
		||||
                        //console.log('Scatter.isTap')
 | 
			
		||||
                        let point = interaction.ended.get(key);
 | 
			
		||||
                        this.onTap(event, interaction, point);
 | 
			
		||||
                    }
 | 
			
		||||
@ -7487,9 +7552,9 @@
 | 
			
		||||
                        rotate: 0,
 | 
			
		||||
                        about: null,
 | 
			
		||||
                        fast: false,
 | 
			
		||||
                        type: END,
 | 
			
		||||
                        type: END
 | 
			
		||||
                    });
 | 
			
		||||
                    this.onTransform.forEach(function (f) {
 | 
			
		||||
                    this.onTransform.forEach(function(f) {
 | 
			
		||||
                        f(event);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
@ -7503,7 +7568,7 @@
 | 
			
		||||
        //onTap(event, interaction, point) {}
 | 
			
		||||
 | 
			
		||||
        onTap(event, interaction, point) {
 | 
			
		||||
            console.log('AbstractScatter.onTap', this.tapDelegate, interaction);
 | 
			
		||||
            //console.log('AbstractScatter.onTap', this.tapDelegate, interaction)
 | 
			
		||||
            if (this.tapDelegate) {
 | 
			
		||||
                Events$1.stop(event);
 | 
			
		||||
                this.tapDelegate.tap(event, 'scatter');
 | 
			
		||||
@ -7517,9 +7582,9 @@
 | 
			
		||||
                    translate: delta,
 | 
			
		||||
                    scale: this.scale,
 | 
			
		||||
                    about: this.currentAbout,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -7531,9 +7596,9 @@
 | 
			
		||||
                    scale: this.scale,
 | 
			
		||||
                    about: this.currentAbout,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -7545,9 +7610,9 @@
 | 
			
		||||
                    translate: { x: dx, y: dy },
 | 
			
		||||
                    about: about,
 | 
			
		||||
                    fast: true,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -7558,9 +7623,9 @@
 | 
			
		||||
                let event = new ScatterEvent(this, {
 | 
			
		||||
                    scale: this.scale,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -7574,9 +7639,9 @@
 | 
			
		||||
                    scale: this.scale,
 | 
			
		||||
                    about: about,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: null,
 | 
			
		||||
                    type: null
 | 
			
		||||
                });
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -7623,7 +7688,7 @@
 | 
			
		||||
                scaleCloseBuffer = 0.05,
 | 
			
		||||
                useLowPassFilter = false,
 | 
			
		||||
                maxRotation = Angle.degree2radian(15),
 | 
			
		||||
                minInteractionDistance = 200,
 | 
			
		||||
                minInteractionDistance = 200
 | 
			
		||||
            } = {}
 | 
			
		||||
        ) {
 | 
			
		||||
            super({
 | 
			
		||||
@ -7650,7 +7715,7 @@
 | 
			
		||||
                onClose,
 | 
			
		||||
                useLowPassFilter,
 | 
			
		||||
                maxRotation,
 | 
			
		||||
                minInteractionDistance,
 | 
			
		||||
                minInteractionDistance
 | 
			
		||||
            });
 | 
			
		||||
            if (container == null || width == null || height == null) {
 | 
			
		||||
                throw new Error('Invalid value: null')
 | 
			
		||||
@ -7678,7 +7743,7 @@
 | 
			
		||||
                height: height,
 | 
			
		||||
                scale: startScale,
 | 
			
		||||
                rotation: this.startRotationDegrees,
 | 
			
		||||
                transformOrigin: transformOrigin,
 | 
			
		||||
                transformOrigin: transformOrigin
 | 
			
		||||
            };
 | 
			
		||||
            this.tapNodes = new Map();
 | 
			
		||||
 | 
			
		||||
@ -7700,15 +7765,15 @@
 | 
			
		||||
                button.className = 'interactiveElement';
 | 
			
		||||
                this.element.appendChild(button);
 | 
			
		||||
 | 
			
		||||
                button.addEventListener('pointerdown', (e) => {
 | 
			
		||||
                button.addEventListener('pointerdown', e => {
 | 
			
		||||
                    this.startResize(e);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                button.addEventListener('pointermove', (e) => {
 | 
			
		||||
                button.addEventListener('pointermove', e => {
 | 
			
		||||
                    this.resize(e);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                button.addEventListener('pointerup', (e) => {
 | 
			
		||||
                button.addEventListener('pointerup', e => {
 | 
			
		||||
                    this.stopResize(e);
 | 
			
		||||
                });
 | 
			
		||||
                this.resizeButton = button;
 | 
			
		||||
@ -7725,7 +7790,7 @@
 | 
			
		||||
                scale: this.scale,
 | 
			
		||||
                x: this.x,
 | 
			
		||||
                y: this.y,
 | 
			
		||||
                rotation: this.rotation,
 | 
			
		||||
                rotation: this.rotation
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -7776,7 +7841,7 @@
 | 
			
		||||
                top: rect.top - stage.top,
 | 
			
		||||
                left: rect.left - stage.left,
 | 
			
		||||
                width: rect.width,
 | 
			
		||||
                height: rect.height,
 | 
			
		||||
                height: rect.height
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -7817,7 +7882,7 @@
 | 
			
		||||
        set scale(scale) {
 | 
			
		||||
            TweenLite.set(this.element, {
 | 
			
		||||
                scale: scale,
 | 
			
		||||
                transformOrigin: this.transformOrigin,
 | 
			
		||||
                transformOrigin: this.transformOrigin
 | 
			
		||||
            });
 | 
			
		||||
            this._scale = scale;
 | 
			
		||||
        }
 | 
			
		||||
@ -7849,9 +7914,9 @@
 | 
			
		||||
        hide() {
 | 
			
		||||
            TweenLite.to(this.element, 0.1, {
 | 
			
		||||
                display: 'none',
 | 
			
		||||
                onComplete: (e) => {
 | 
			
		||||
                onComplete: e => {
 | 
			
		||||
                    this.element.parentNode.removeChild(this.element);
 | 
			
		||||
                },
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -7865,7 +7930,7 @@
 | 
			
		||||
                x: p.x,
 | 
			
		||||
                y: p.y,
 | 
			
		||||
                rotation: rotationDegrees,
 | 
			
		||||
                transformOrigin: this.transformOrigin,
 | 
			
		||||
                transformOrigin: this.transformOrigin
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -7926,7 +7991,7 @@
 | 
			
		||||
 | 
			
		||||
            let oldPostition = {
 | 
			
		||||
                x: this.element.getBoundingClientRect().left,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top
 | 
			
		||||
            };
 | 
			
		||||
            this.bringToFront();
 | 
			
		||||
 | 
			
		||||
@ -7934,7 +7999,7 @@
 | 
			
		||||
 | 
			
		||||
            let newPostition = {
 | 
			
		||||
                x: this.element.getBoundingClientRect().left,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            let offset = Points.subtract(oldPostition, newPostition);
 | 
			
		||||
@ -7979,7 +8044,7 @@
 | 
			
		||||
                )
 | 
			
		||||
                    TweenLite.to(this.element, 0, {
 | 
			
		||||
                        width: this.element.offsetWidth + resizeW / this.scale,
 | 
			
		||||
                        height: this.element.offsetHeight + resizeH / this.scale,
 | 
			
		||||
                        height: this.element.offsetHeight + resizeH / this.scale
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                this.oldX = e.clientX;
 | 
			
		||||
@ -7996,12 +8061,12 @@
 | 
			
		||||
            let event = new CustomEvent('resizeEnded');
 | 
			
		||||
            let oldPostition = {
 | 
			
		||||
                x: this.element.getBoundingClientRect().left,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top
 | 
			
		||||
            };
 | 
			
		||||
            this.element.style.transformOrigin = '50% 50%';
 | 
			
		||||
            let newPostition = {
 | 
			
		||||
                x: this.element.getBoundingClientRect().left,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top,
 | 
			
		||||
                y: this.element.getBoundingClientRect().top
 | 
			
		||||
            };
 | 
			
		||||
            let offset = Points.subtract(oldPostition, newPostition);
 | 
			
		||||
 | 
			
		||||
@ -8174,7 +8239,6 @@
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										0
									
								
								lib/_menu.js
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								lib/_menu.js
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							@ -766,7 +766,6 @@ export default class Card {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static _calculateCenterRelativeTo(target, image) {
 | 
			
		||||
        // console.log('_calculateCenterRelativeTo', target, image)
 | 
			
		||||
        let bbox = image.getBBox()
 | 
			
		||||
        let width = bbox.width
 | 
			
		||||
        let height = bbox.height
 | 
			
		||||
 | 
			
		||||
@ -213,7 +213,6 @@ export default class Highlight extends Object {
 | 
			
		||||
                image,
 | 
			
		||||
            })
 | 
			
		||||
            let center = this._calculateCenterRelativeTo(target, image)
 | 
			
		||||
            console.log('_calculateCenterRelativeTo', center)
 | 
			
		||||
            TweenLite.set(maskImage, {
 | 
			
		||||
                transformOrigin: `${center.x} ${center.y}`,
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
@ -158,7 +158,6 @@ export default class CardWrapper extends Object {
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
@ -17,44 +17,60 @@
 | 
			
		||||
                    <div class="flipFace back" style="visibility: hidden"></div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <!-- Very tricky problem to scale svgs: see https://css-tricks.com/scale-svg/ -->
 | 
			
		||||
                <svg
 | 
			
		||||
                    class="flipButton backBtn"
 | 
			
		||||
                    style="visibility: hidden"
 | 
			
		||||
                    viewBox="0 0 100 100"
 | 
			
		||||
                    preserveAspectRatio="xMidYMid meet"
 | 
			
		||||
                >
 | 
			
		||||
                <!-- SVG viewPort interferes with DOMMatrix calculations: see  
 | 
			
		||||
                    https://stackoverflow.com/questions/70696387/how-to-get-transform-matrix-of-a-dom-element-->
 | 
			
		||||
            <div class="flipButton backBtn" style="visibility:hidden;">
 | 
			
		||||
                <svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet" style="width:inherit; height:inherit;">
 | 
			
		||||
                    <g stroke-width="8" stroke="white">
 | 
			
		||||
                        <circle cx="50" cy="50" r="44" fill="gray" />
 | 
			
		||||
                        <line x1="30" y1="30" x2="70" y2="70" />
 | 
			
		||||
                        <line x1="30" y1="70" x2="70" y2="30" />
 | 
			
		||||
                        <circle  cx="50" cy="50" r="44" fill="gray" />
 | 
			
		||||
                        <line x1="30" y1="30" x2="70" y2="70"  />
 | 
			
		||||
                        <line x1="30" y1="70" x2="70" y2="30"  />
 | 
			
		||||
                    </g>
 | 
			
		||||
                </svg>
 | 
			
		||||
            </div>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
                <svg class="flipButton infoBtn" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
 | 
			
		||||
            <div class="flipButton infoBtn">
 | 
			
		||||
                <svg  viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet"  style="width:inherit; height:inherit;">
 | 
			
		||||
                    <circle cx="50" cy="50" r="44" stroke="white" stroke-width="8" fill="gray" />
 | 
			
		||||
                    <circle cx="50" cy="32" r="7" fill="white" />
 | 
			
		||||
                    <line x1="50" y1="46" x2="50" y2="78" stroke="white" stroke-width="12" />
 | 
			
		||||
                </svg>
 | 
			
		||||
            </div>
 | 
			
		||||
        </template>
 | 
			
		||||
    </head>
 | 
			
		||||
    <body onload="Doctest.run()">
 | 
			
		||||
        <h1><a href="index.html">lib.</a>Flippable</h1>
 | 
			
		||||
        <p>
 | 
			
		||||
            The flip effect simulates a flip between a front and back view of an object by means of a 3D rotation. The
 | 
			
		||||
            DOMFlippable class implements this effect for two DOM nodes, one as the front view, the other as the back
 | 
			
		||||
            view. Both views are connected via a HTML template that defines the placeholders for front and back views.
 | 
			
		||||
            The style file "css/flipeffect.css" holds reasonable default styles for this kind of templates.
 | 
			
		||||
        </p>
 | 
			
		||||
        <pre><code>
 | 
			
		||||
            
 | 
			
		||||
        </div>
 | 
			
		||||
    </template>
 | 
			
		||||
 | 
			
		||||
</head>
 | 
			
		||||
<body onload="Doctest.run()">
 | 
			
		||||
<h1>
 | 
			
		||||
	Flippable
 | 
			
		||||
</h1>
 | 
			
		||||
<p>
 | 
			
		||||
The flip effect simulates a flip between a front and back view of an object
 | 
			
		||||
by means of a 3D rotation. The DOMFlippable class implements this effect for two
 | 
			
		||||
DOM nodes, one as the front view, the other as the back view. Both views are connected
 | 
			
		||||
via a HTML template that defines the placeholders for front and back views. The
 | 
			
		||||
style file "css/flipeffect.css" holds reasonable default styles for this kind of
 | 
			
		||||
templates.
 | 
			
		||||
</p>
 | 
			
		||||
<p>
 | 
			
		||||
The SVG buttons have to be wrapped in an HTML DOM element which handles events. Otherwise, 
 | 
			
		||||
the viewbox of the SVG will interfere with the coordinate transformation.
 | 
			
		||||
</p>
 | 
			
		||||
<pre><code>
 | 
			
		||||
    <template id="flipTemplate">
 | 
			
		||||
        <div class="flipWrapper">
 | 
			
		||||
            <div class="flipCard">
 | 
			
		||||
                <div class="flipFace front"></div>
 | 
			
		||||
                <div class="flipFace back" style="visibility:hidden;"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <svg class="flipButton backBtn" .../>
 | 
			
		||||
            <svg class="flipButton infoBtn" .../>
 | 
			
		||||
            <div class="flipButton backBtn" .../>
 | 
			
		||||
                <svg .../>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="flipButton infoBtn" .../>
 | 
			
		||||
                <svg .../>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </template>
 | 
			
		||||
</code>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										191
									
								
								lib/scatter.js
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								lib/scatter.js
									
									
									
									
									
								
							@ -88,6 +88,40 @@ export class ResizeEvent extends BaseEvent {
 | 
			
		||||
 *
 | 
			
		||||
 * @constructor
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RequestFrameThrower {
 | 
			
		||||
    /** Implemenents the standard throw behavior. */
 | 
			
		||||
    animateThrow(throwable) {
 | 
			
		||||
        /*** Calls the animateThrow method in sync with the display link. */
 | 
			
		||||
        requestAnimationFrame(throwable.animateThrow.bind(throwable))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class TimeoutThrower {
 | 
			
		||||
 | 
			
		||||
    constructor(delay=20) {
 | 
			
		||||
        this.throwables = new Set()
 | 
			
		||||
        this.delay = delay
 | 
			
		||||
        setTimeout(this.animateStep.bind(this), this.delay)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    animateThrow(throwable) {
 | 
			
		||||
        this.throwables.add(throwable)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    animateStep() {
 | 
			
		||||
        let active = [...this.throwables]
 | 
			
		||||
        this.throwables.clear()
 | 
			
		||||
        for(let throwable of active) {
 | 
			
		||||
            throwable.animateThrow()
 | 
			
		||||
        }
 | 
			
		||||
        setTimeout(this.animateStep.bind(this), this.delay)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let thrower = new RequestFrameThrower()
 | 
			
		||||
 | 
			
		||||
class Throwable {
 | 
			
		||||
    constructor({
 | 
			
		||||
        movableX = true,
 | 
			
		||||
@ -95,7 +129,7 @@ class Throwable {
 | 
			
		||||
        throwVisibility = 44,
 | 
			
		||||
        throwDamping = 0.95,
 | 
			
		||||
        autoThrow = true,
 | 
			
		||||
        onThrowFinished = null,
 | 
			
		||||
        onThrowFinished = null
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        this.movableX = movableX
 | 
			
		||||
        this.movableY = movableY
 | 
			
		||||
@ -104,9 +138,16 @@ class Throwable {
 | 
			
		||||
        this.autoThrow = autoThrow
 | 
			
		||||
        this.velocities = []
 | 
			
		||||
        this.velocity = null
 | 
			
		||||
        this.timestamp = null
 | 
			
		||||
        this.lastframe = null
 | 
			
		||||
        this.onThrowFinished = onThrowFinished
 | 
			
		||||
        //console.log("onThrowFinished", onThrowFinished)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static defaultThrow() {
 | 
			
		||||
        thrower = new RequestFrameThrower()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static timeoutThrow() {
 | 
			
		||||
        thrower = new TimeoutThrower()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observeVelocity() {
 | 
			
		||||
@ -128,7 +169,7 @@ class Throwable {
 | 
			
		||||
                t: t,
 | 
			
		||||
                dt: dt,
 | 
			
		||||
                dx: delta.x / number,
 | 
			
		||||
                dy: delta.y / number,
 | 
			
		||||
                dy: delta.y / number
 | 
			
		||||
            }
 | 
			
		||||
            this.velocities.push(velocity)
 | 
			
		||||
            while (this.velocities.length > buffer) {
 | 
			
		||||
@ -137,8 +178,23 @@ class Throwable {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addTestVelocity(delta, dt=20, buffer = 5) {
 | 
			
		||||
        let t = performance.now()
 | 
			
		||||
        this.lastframe = t
 | 
			
		||||
        let velocity = {
 | 
			
		||||
            t: t,
 | 
			
		||||
            dt: dt,
 | 
			
		||||
            dx: delta.x ,
 | 
			
		||||
            dy: delta.y 
 | 
			
		||||
        }
 | 
			
		||||
        for(let i=0; i<buffer; i++) {
 | 
			
		||||
            velocity.t += dt
 | 
			
		||||
            this.velocities.push(velocity)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    meanVelocity(milliseconds = 30) {
 | 
			
		||||
        this.addVelocity({ x: 0, y: 0, number: 1 })
 | 
			
		||||
     //   this.addVelocity({ x: 0, y: 0, number: 1 })
 | 
			
		||||
        let sum = { x: 0, y: 0 }
 | 
			
		||||
        let count = 0
 | 
			
		||||
        let t = 0
 | 
			
		||||
@ -159,6 +215,7 @@ class Throwable {
 | 
			
		||||
    killAnimation() {
 | 
			
		||||
        this.velocity = null
 | 
			
		||||
        this.velocities = []
 | 
			
		||||
        this.lastframe = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    startThrow() {
 | 
			
		||||
@ -180,6 +237,10 @@ class Throwable {
 | 
			
		||||
        return dt
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    recurseAnimateThrow() {
 | 
			
		||||
        ThrowableObjects.add(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    animateThrow(time) {
 | 
			
		||||
        if (this.velocity != null) {
 | 
			
		||||
            let dt = this._throwDeltaTime()
 | 
			
		||||
@ -190,7 +251,7 @@ class Throwable {
 | 
			
		||||
            if (nextLength > prevLength) {
 | 
			
		||||
                let factor = nextLength / prevLength
 | 
			
		||||
                next = Points.multiplyScalar(next, 1 / factor)
 | 
			
		||||
                console.log('Prevent acceleration', factor, this.velocity, next)
 | 
			
		||||
                // console.log('Prevent acceleration', factor, this.velocity, next)
 | 
			
		||||
            }
 | 
			
		||||
            this.velocity = next
 | 
			
		||||
            let d = Points.multiplyScalar(this.velocity, dt)
 | 
			
		||||
@ -198,11 +259,11 @@ class Throwable {
 | 
			
		||||
 | 
			
		||||
            this.onDragUpdate(d)
 | 
			
		||||
            if (dt == 0 || this.needsAnimation()) {
 | 
			
		||||
                requestAnimationFrame(this.animateThrow.bind(this))
 | 
			
		||||
                thrower.animateThrow(this)
 | 
			
		||||
                return
 | 
			
		||||
            } else {
 | 
			
		||||
                if (this.isOutside()) {
 | 
			
		||||
                    requestAnimationFrame(this.animateThrow.bind(this))
 | 
			
		||||
                    thrower.animateThrow(this)
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -226,7 +287,7 @@ class Throwable {
 | 
			
		||||
        let next = Points.multiplyScalar(velocity, this.throwDamping)
 | 
			
		||||
        return {
 | 
			
		||||
            x: this.movableX ? next.x : 0,
 | 
			
		||||
            y: this.movableY ? next.y : 0,
 | 
			
		||||
            y: this.movableY ? next.y : 0
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -271,7 +332,7 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
        scaleCloseBuffer = 0.05,
 | 
			
		||||
        maxRotation = Angle.degree2radian(5),
 | 
			
		||||
        minInteractionDistance = 0,
 | 
			
		||||
        useLowPassFilter = false,
 | 
			
		||||
        useLowPassFilter = false
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        if (rotationDegrees != null && rotation != null) {
 | 
			
		||||
            throw new Error('Use rotationDegrees or rotation but not both')
 | 
			
		||||
@ -286,7 +347,7 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
            throwVisibility,
 | 
			
		||||
            throwDamping,
 | 
			
		||||
            autoThrow,
 | 
			
		||||
            onThrowFinished,
 | 
			
		||||
            onThrowFinished
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@ -361,7 +422,7 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
 | 
			
		||||
    _callCloseCallbacks() {
 | 
			
		||||
        if (this.onClose) {
 | 
			
		||||
            this.onClose.forEach((callback) => callback(this))
 | 
			
		||||
            this.onClose.forEach(callback => callback(this))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -381,6 +442,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
        let delta = interaction.delta()
 | 
			
		||||
 | 
			
		||||
        if (delta != null) {
 | 
			
		||||
            /* uo: Is this the best place to add velocity? It works with scrollable text in card drawers but
 | 
			
		||||
            has to be tested */
 | 
			
		||||
            this.addVelocity(delta)
 | 
			
		||||
            let rotate = delta.rotate
 | 
			
		||||
            let zoom = delta.zoom
 | 
			
		||||
            if (this.maxRotation != null) {
 | 
			
		||||
@ -399,9 +463,10 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                zoomDelta *= ratio
 | 
			
		||||
                zoom = 1 + zoomDelta
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.transform(delta, zoom, rotate, delta.about)
 | 
			
		||||
            /* uo: This is too late an dangerous. transform sometimes modifies delta
 | 
			
		||||
            this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
 | 
			
		||||
            */
 | 
			
		||||
 | 
			
		||||
            if (zoom != 1) this.interactionAnchor = delta.about
 | 
			
		||||
        }
 | 
			
		||||
@ -512,11 +577,11 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
            if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) {
 | 
			
		||||
                this.zoom(this.minScale, {
 | 
			
		||||
                    animate: 0.2,
 | 
			
		||||
                    onComplete: this.close.bind(this),
 | 
			
		||||
                    onComplete: this.close.bind(this)
 | 
			
		||||
                })
 | 
			
		||||
            } else if (this.scale < this.minScale + this.scaleCloseThreshold) {
 | 
			
		||||
                this.zoom(this.minScale + this.scaleCloseThreshold, {
 | 
			
		||||
                    animate: 0.4,
 | 
			
		||||
                    animate: 0.4
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
@ -538,12 +603,12 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                    x: '+=' + d.x,
 | 
			
		||||
                    y: '+=' + d.y,
 | 
			
		||||
                    /* scale: scale, uo: not defined, why was this here? */
 | 
			
		||||
                    onUpdate: (e) => {
 | 
			
		||||
                    onUpdate: e => {
 | 
			
		||||
                        let p = this.position
 | 
			
		||||
                        let dx = p.x - startPos.x
 | 
			
		||||
                        let dy = p.x - startPos.y
 | 
			
		||||
                        this.onMoved(dx, dy)
 | 
			
		||||
                    },
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
            } else {
 | 
			
		||||
                this._move(d)
 | 
			
		||||
@ -572,7 +637,7 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                    scale: scale,
 | 
			
		||||
                    delay: delay,
 | 
			
		||||
                    onComplete: onComplete,
 | 
			
		||||
                    onUpdate: this.onZoomed.bind(this),
 | 
			
		||||
                    onUpdate: this.onZoomed.bind(this)
 | 
			
		||||
                })
 | 
			
		||||
            } else {
 | 
			
		||||
                this.scale = scale
 | 
			
		||||
@ -590,7 +655,7 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
    transform(translate, zoom, rotate, anchor) {
 | 
			
		||||
        let delta = {
 | 
			
		||||
            x: this.movableX ? translate.x : 0,
 | 
			
		||||
            y: this.movableY ? translate.y : 0,
 | 
			
		||||
            y: this.movableY ? translate.y : 0
 | 
			
		||||
        }
 | 
			
		||||
        if (this.resizable) var vzoom = zoom
 | 
			
		||||
        if (!this.translatable) delta = { x: 0, y: 0 }
 | 
			
		||||
@ -605,9 +670,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                    rotate: 0,
 | 
			
		||||
                    about: anchor,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: UPDATE,
 | 
			
		||||
                    type: UPDATE
 | 
			
		||||
                })
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event)
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
@ -636,9 +701,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                translate: delta,
 | 
			
		||||
                scale: newScale,
 | 
			
		||||
                rotate: rotate,
 | 
			
		||||
                about: anchor,
 | 
			
		||||
                about: anchor
 | 
			
		||||
            })
 | 
			
		||||
            this.onTransform.forEach(function (f) {
 | 
			
		||||
            this.onTransform.forEach(function(f) {
 | 
			
		||||
                f(event)
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
@ -704,7 +769,7 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
            if (this.scale > this.maxScale) zoom = 1 - amount
 | 
			
		||||
            if (zoom != 1) {
 | 
			
		||||
                this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor)
 | 
			
		||||
                requestAnimationFrame((dt) => {
 | 
			
		||||
                requestAnimationFrame(dt => {
 | 
			
		||||
                    this.animateZoomBounce(dt)
 | 
			
		||||
                })
 | 
			
		||||
                return
 | 
			
		||||
@ -761,9 +826,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                rotate: 0,
 | 
			
		||||
                about: null,
 | 
			
		||||
                fast: false,
 | 
			
		||||
                type: START,
 | 
			
		||||
                type: START
 | 
			
		||||
            })
 | 
			
		||||
            this.onTransform.forEach(function (f) {
 | 
			
		||||
            this.onTransform.forEach(function(f) {
 | 
			
		||||
                f(event)
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
@ -779,13 +844,13 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onEnd(event, interaction) {
 | 
			
		||||
        console.log('Scatter.onEnd', this.dragging)
 | 
			
		||||
        //console.log('Scatter.onEnd', this.dragging)
 | 
			
		||||
        if (interaction.isFinished()) {
 | 
			
		||||
            this.endGesture(interaction)
 | 
			
		||||
            this.dragging = false
 | 
			
		||||
            for (let key of interaction.ended.keys()) {
 | 
			
		||||
                if (interaction.isTap(key)) {
 | 
			
		||||
                    console.log('Scatter.isTap')
 | 
			
		||||
                    //console.log('Scatter.isTap')
 | 
			
		||||
                    let point = interaction.ended.get(key)
 | 
			
		||||
                    this.onTap(event, interaction, point)
 | 
			
		||||
                }
 | 
			
		||||
@ -797,9 +862,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                    rotate: 0,
 | 
			
		||||
                    about: null,
 | 
			
		||||
                    fast: false,
 | 
			
		||||
                    type: END,
 | 
			
		||||
                    type: END
 | 
			
		||||
                })
 | 
			
		||||
                this.onTransform.forEach(function (f) {
 | 
			
		||||
                this.onTransform.forEach(function(f) {
 | 
			
		||||
                    f(event)
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
@ -813,7 +878,7 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
    //onTap(event, interaction, point) {}
 | 
			
		||||
 | 
			
		||||
    onTap(event, interaction, point) {
 | 
			
		||||
        console.log('AbstractScatter.onTap', this.tapDelegate, interaction)
 | 
			
		||||
        //console.log('AbstractScatter.onTap', this.tapDelegate, interaction)
 | 
			
		||||
        if (this.tapDelegate) {
 | 
			
		||||
            Events.stop(event)
 | 
			
		||||
            this.tapDelegate.tap(event, 'scatter')
 | 
			
		||||
@ -827,9 +892,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                translate: delta,
 | 
			
		||||
                scale: this.scale,
 | 
			
		||||
                about: this.currentAbout,
 | 
			
		||||
                type: null,
 | 
			
		||||
                type: null
 | 
			
		||||
            })
 | 
			
		||||
            this.onTransform.forEach(function (f) {
 | 
			
		||||
            this.onTransform.forEach(function(f) {
 | 
			
		||||
                f(event)
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
@ -841,9 +906,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                scale: this.scale,
 | 
			
		||||
                about: this.currentAbout,
 | 
			
		||||
                fast: false,
 | 
			
		||||
                type: null,
 | 
			
		||||
                type: null
 | 
			
		||||
            })
 | 
			
		||||
            this.onTransform.forEach(function (f) {
 | 
			
		||||
            this.onTransform.forEach(function(f) {
 | 
			
		||||
                f(event)
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
@ -855,9 +920,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                translate: { x: dx, y: dy },
 | 
			
		||||
                about: about,
 | 
			
		||||
                fast: true,
 | 
			
		||||
                type: null,
 | 
			
		||||
                type: null
 | 
			
		||||
            })
 | 
			
		||||
            this.onTransform.forEach(function (f) {
 | 
			
		||||
            this.onTransform.forEach(function(f) {
 | 
			
		||||
                f(event)
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
@ -868,9 +933,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
            let event = new ScatterEvent(this, {
 | 
			
		||||
                scale: this.scale,
 | 
			
		||||
                fast: false,
 | 
			
		||||
                type: null,
 | 
			
		||||
                type: null
 | 
			
		||||
            })
 | 
			
		||||
            this.onTransform.forEach(function (f) {
 | 
			
		||||
            this.onTransform.forEach(function(f) {
 | 
			
		||||
                f(event)
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
@ -884,9 +949,9 @@ export class AbstractScatter extends Throwable {
 | 
			
		||||
                scale: this.scale,
 | 
			
		||||
                about: about,
 | 
			
		||||
                fast: false,
 | 
			
		||||
                type: null,
 | 
			
		||||
                type: null
 | 
			
		||||
            })
 | 
			
		||||
            this.onTransform.forEach(function (f) {
 | 
			
		||||
            this.onTransform.forEach(function(f) {
 | 
			
		||||
                f(event)
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
@ -918,7 +983,7 @@ export class DOMScatterContainer {
 | 
			
		||||
            useCapture = true,
 | 
			
		||||
            capturePointerEvents = true,
 | 
			
		||||
            touchAction = 'none',
 | 
			
		||||
            debugCanvas = null,
 | 
			
		||||
            debugCanvas = null
 | 
			
		||||
        } = {}
 | 
			
		||||
    ) {
 | 
			
		||||
        this.onCapture = null
 | 
			
		||||
@ -930,7 +995,7 @@ export class DOMScatterContainer {
 | 
			
		||||
            movement of scatter objects, the touchmove event has to be bound again.
 | 
			
		||||
            */
 | 
			
		||||
            if (Capabilities.isSafari) {
 | 
			
		||||
                document.addEventListener('touchmove', (event) => this.preventPinch(event), false)
 | 
			
		||||
                document.addEventListener('touchmove', event => this.preventPinch(event), false)
 | 
			
		||||
                stopEvents = false
 | 
			
		||||
            } else {
 | 
			
		||||
                stopEvents = true
 | 
			
		||||
@ -945,11 +1010,11 @@ export class DOMScatterContainer {
 | 
			
		||||
        this.delegate = new InteractionMapper(element, this, {
 | 
			
		||||
            useCapture,
 | 
			
		||||
            capturePointerEvents,
 | 
			
		||||
            mouseWheelElement: window,
 | 
			
		||||
            mouseWheelElement: window
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        if (debugCanvas !== null) {
 | 
			
		||||
            requestAnimationFrame((dt) => {
 | 
			
		||||
            requestAnimationFrame(dt => {
 | 
			
		||||
                this.showTouches(dt, debugCanvas)
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
@ -971,7 +1036,7 @@ export class DOMScatterContainer {
 | 
			
		||||
            context.fill()
 | 
			
		||||
            context.stroke()
 | 
			
		||||
        }
 | 
			
		||||
        requestAnimationFrame((dt) => {
 | 
			
		||||
        requestAnimationFrame(dt => {
 | 
			
		||||
            this.showTouches(dt, canvas)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
@ -1113,7 +1178,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
            scaleCloseBuffer = 0.05,
 | 
			
		||||
            useLowPassFilter = false,
 | 
			
		||||
            maxRotation = Angle.degree2radian(15),
 | 
			
		||||
            minInteractionDistance = 200,
 | 
			
		||||
            minInteractionDistance = 200
 | 
			
		||||
        } = {}
 | 
			
		||||
    ) {
 | 
			
		||||
        super({
 | 
			
		||||
@ -1140,7 +1205,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
            onClose,
 | 
			
		||||
            useLowPassFilter,
 | 
			
		||||
            maxRotation,
 | 
			
		||||
            minInteractionDistance,
 | 
			
		||||
            minInteractionDistance
 | 
			
		||||
        })
 | 
			
		||||
        if (container == null || width == null || height == null) {
 | 
			
		||||
            throw new Error('Invalid value: null')
 | 
			
		||||
@ -1168,7 +1233,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
            height: height,
 | 
			
		||||
            scale: startScale,
 | 
			
		||||
            rotation: this.startRotationDegrees,
 | 
			
		||||
            transformOrigin: transformOrigin,
 | 
			
		||||
            transformOrigin: transformOrigin
 | 
			
		||||
        }
 | 
			
		||||
        this.tapNodes = new Map()
 | 
			
		||||
 | 
			
		||||
@ -1190,15 +1255,15 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
            button.className = 'interactiveElement'
 | 
			
		||||
            this.element.appendChild(button)
 | 
			
		||||
 | 
			
		||||
            button.addEventListener('pointerdown', (e) => {
 | 
			
		||||
            button.addEventListener('pointerdown', e => {
 | 
			
		||||
                this.startResize(e)
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            button.addEventListener('pointermove', (e) => {
 | 
			
		||||
            button.addEventListener('pointermove', e => {
 | 
			
		||||
                this.resize(e)
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            button.addEventListener('pointerup', (e) => {
 | 
			
		||||
            button.addEventListener('pointerup', e => {
 | 
			
		||||
                this.stopResize(e)
 | 
			
		||||
            })
 | 
			
		||||
            this.resizeButton = button
 | 
			
		||||
@ -1215,7 +1280,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
            scale: this.scale,
 | 
			
		||||
            x: this.x,
 | 
			
		||||
            y: this.y,
 | 
			
		||||
            rotation: this.rotation,
 | 
			
		||||
            rotation: this.rotation
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1266,7 +1331,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
            top: rect.top - stage.top,
 | 
			
		||||
            left: rect.left - stage.left,
 | 
			
		||||
            width: rect.width,
 | 
			
		||||
            height: rect.height,
 | 
			
		||||
            height: rect.height
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1307,7 +1372,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
    set scale(scale) {
 | 
			
		||||
        TweenLite.set(this.element, {
 | 
			
		||||
            scale: scale,
 | 
			
		||||
            transformOrigin: this.transformOrigin,
 | 
			
		||||
            transformOrigin: this.transformOrigin
 | 
			
		||||
        })
 | 
			
		||||
        this._scale = scale
 | 
			
		||||
    }
 | 
			
		||||
@ -1339,9 +1404,9 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
    hide() {
 | 
			
		||||
        TweenLite.to(this.element, 0.1, {
 | 
			
		||||
            display: 'none',
 | 
			
		||||
            onComplete: (e) => {
 | 
			
		||||
            onComplete: e => {
 | 
			
		||||
                this.element.parentNode.removeChild(this.element)
 | 
			
		||||
            },
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1355,7 +1420,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
            x: p.x,
 | 
			
		||||
            y: p.y,
 | 
			
		||||
            rotation: rotationDegrees,
 | 
			
		||||
            transformOrigin: this.transformOrigin,
 | 
			
		||||
            transformOrigin: this.transformOrigin
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1416,7 +1481,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
 | 
			
		||||
        let oldPostition = {
 | 
			
		||||
            x: this.element.getBoundingClientRect().left,
 | 
			
		||||
            y: this.element.getBoundingClientRect().top,
 | 
			
		||||
            y: this.element.getBoundingClientRect().top
 | 
			
		||||
        }
 | 
			
		||||
        this.bringToFront()
 | 
			
		||||
 | 
			
		||||
@ -1424,7 +1489,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
 | 
			
		||||
        let newPostition = {
 | 
			
		||||
            x: this.element.getBoundingClientRect().left,
 | 
			
		||||
            y: this.element.getBoundingClientRect().top,
 | 
			
		||||
            y: this.element.getBoundingClientRect().top
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let offset = Points.subtract(oldPostition, newPostition)
 | 
			
		||||
@ -1469,7 +1534,7 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
            )
 | 
			
		||||
                TweenLite.to(this.element, 0, {
 | 
			
		||||
                    width: this.element.offsetWidth + resizeW / this.scale,
 | 
			
		||||
                    height: this.element.offsetHeight + resizeH / this.scale,
 | 
			
		||||
                    height: this.element.offsetHeight + resizeH / this.scale
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
            this.oldX = e.clientX
 | 
			
		||||
@ -1486,12 +1551,12 @@ export class DOMScatter extends AbstractScatter {
 | 
			
		||||
        let event = new CustomEvent('resizeEnded')
 | 
			
		||||
        let oldPostition = {
 | 
			
		||||
            x: this.element.getBoundingClientRect().left,
 | 
			
		||||
            y: this.element.getBoundingClientRect().top,
 | 
			
		||||
            y: this.element.getBoundingClientRect().top
 | 
			
		||||
        }
 | 
			
		||||
        this.element.style.transformOrigin = '50% 50%'
 | 
			
		||||
        let newPostition = {
 | 
			
		||||
            x: this.element.getBoundingClientRect().left,
 | 
			
		||||
            y: this.element.getBoundingClientRect().top,
 | 
			
		||||
            y: this.element.getBoundingClientRect().top
 | 
			
		||||
        }
 | 
			
		||||
        let offset = Points.subtract(oldPostition, newPostition)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user