1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753 |
- (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.panzoom = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
- 'use strict';
- /* globals SVGElement */
- /**
- * Allows to drag and zoom svg elements
- */
- var wheel = require('wheel');
- var animate = require('amator');
- var eventify = require('ngraph.events');
- var kinetic = require('./lib/kinetic.js');
- var createTextSelectionInterceptor = require('./lib/createTextSelectionInterceptor.js');
- var domTextSelectionInterceptor = createTextSelectionInterceptor();
- var fakeTextSelectorInterceptor = createTextSelectionInterceptor(true);
- var Transform = require('./lib/transform.js');
- var makeSvgController = require('./lib/svgController.js');
- var makeDomController = require('./lib/domController.js');
- var defaultZoomSpeed = 1;
- var defaultDoubleTapZoomSpeed = 1.75;
- var doubleTapSpeedInMS = 300;
- module.exports = createPanZoom;
- /**
- * Creates a new instance of panzoom, so that an object can be panned and zoomed
- *
- * @param {DOMElement} domElement where panzoom should be attached.
- * @param {Object} options that configure behavior.
- */
- function createPanZoom(domElement, options) {
- options = options || {};
- var panController = options.controller;
- if (!panController) {
- if (domElement instanceof SVGElement) {
- panController = makeSvgController(domElement, options);
- }
- if (domElement instanceof HTMLElement) {
- panController = makeDomController(domElement, options);
- }
- }
- if (!panController) {
- throw new Error(
- 'Cannot create panzoom for the current type of dom element'
- );
- }
- var owner = panController.getOwner();
- // just to avoid GC pressure, every time we do intermediate transform
- // we return this object. For internal use only. Never give it back to the consumer of this library
- var storedCTMResult = { x: 0, y: 0 };
- var isDirty = false;
- var transform = new Transform();
- if (panController.initTransform) {
- panController.initTransform(transform);
- }
- var filterKey = typeof options.filterKey === 'function' ? options.filterKey : noop;
- // TODO: likely need to unite pinchSpeed with zoomSpeed
- var pinchSpeed = typeof options.pinchSpeed === 'number' ? options.pinchSpeed : 1;
- var bounds = options.bounds;
- var maxZoom = typeof options.maxZoom === 'number' ? options.maxZoom : Number.POSITIVE_INFINITY;
- var minZoom = typeof options.minZoom === 'number' ? options.minZoom : 0;
- var boundsPadding = typeof options.boundsPadding === 'number' ? options.boundsPadding : 0.05;
- var zoomDoubleClickSpeed = typeof options.zoomDoubleClickSpeed === 'number' ? options.zoomDoubleClickSpeed : defaultDoubleTapZoomSpeed;
- var beforeWheel = options.beforeWheel || noop;
- var beforeMouseDown = options.beforeMouseDown || noop;
- var speed = typeof options.zoomSpeed === 'number' ? options.zoomSpeed : defaultZoomSpeed;
- var transformOrigin = parseTransformOrigin(options.transformOrigin);
- var textSelection = options.enableTextSelection ? fakeTextSelectorInterceptor : domTextSelectionInterceptor;
- validateBounds(bounds);
- if (options.autocenter) {
- autocenter();
- }
- var frameAnimation;
- var lastTouchEndTime = 0;
- var lastSingleFingerOffset;
- var touchInProgress = false;
- // We only need to fire panstart when actual move happens
- var panstartFired = false;
- // cache mouse coordinates here
- var mouseX;
- var mouseY;
- var pinchZoomLength;
- var smoothScroll;
- if ('smoothScroll' in options && !options.smoothScroll) {
- // If user explicitly asked us not to use smooth scrolling, we obey
- smoothScroll = rigidScroll();
- } else {
- // otherwise we use forward smoothScroll settings to kinetic API
- // which makes scroll smoothing.
- smoothScroll = kinetic(getPoint, scroll, options.smoothScroll);
- }
- var moveByAnimation;
- var zoomToAnimation;
- var multiTouch;
- var paused = false;
- listenForEvents();
- var api = {
- dispose: dispose,
- moveBy: internalMoveBy,
- moveTo: moveTo,
- centerOn: centerOn,
- zoomTo: publicZoomTo,
- zoomAbs: zoomAbs,
- smoothZoom: smoothZoom,
- smoothZoomAbs: smoothZoomAbs,
- showRectangle: showRectangle,
- pause: pause,
- resume: resume,
- isPaused: isPaused,
- getTransform: getTransformModel,
- getMinZoom: getMinZoom,
- setMinZoom: setMinZoom,
- getMaxZoom: getMaxZoom,
- setMaxZoom: setMaxZoom,
- getTransformOrigin: getTransformOrigin,
- setTransformOrigin: setTransformOrigin,
- getZoomSpeed: getZoomSpeed,
- setZoomSpeed: setZoomSpeed,
- getOwner: () => owner
- };
- eventify(api);
- return api;
- function pause() {
- releaseEvents();
- paused = true;
- }
- function resume() {
- if (paused) {
- listenForEvents();
- paused = false;
- }
- }
- function isPaused() {
- return paused;
- }
- function showRectangle(rect) {
- // TODO: this duplicates autocenter. I think autocenter should go.
- var clientRect = owner.getBoundingClientRect();
- var size = transformToScreen(clientRect.width, clientRect.height);
- var rectWidth = rect.right - rect.left;
- var rectHeight = rect.bottom - rect.top;
- if (!Number.isFinite(rectWidth) || !Number.isFinite(rectHeight)) {
- throw new Error('Invalid rectangle');
- }
- var dw = size.x / rectWidth;
- var dh = size.y / rectHeight;
- var scale = Math.min(dw, dh);
- transform.x = -(rect.left + rectWidth / 2) * scale + size.x / 2;
- transform.y = -(rect.top + rectHeight / 2) * scale + size.y / 2;
- transform.scale = scale;
- }
- function transformToScreen(x, y) {
- if (panController.getScreenCTM) {
- var parentCTM = panController.getScreenCTM();
- var parentScaleX = parentCTM.a;
- var parentScaleY = parentCTM.d;
- var parentOffsetX = parentCTM.e;
- var parentOffsetY = parentCTM.f;
- storedCTMResult.x = x * parentScaleX - parentOffsetX;
- storedCTMResult.y = y * parentScaleY - parentOffsetY;
- } else {
- storedCTMResult.x = x;
- storedCTMResult.y = y;
- }
- return storedCTMResult;
- }
- function autocenter() {
- var w; // width of the parent
- var h; // height of the parent
- var left = 0;
- var top = 0;
- var sceneBoundingBox = getBoundingBox();
- if (sceneBoundingBox) {
- // If we have bounding box - use it.
- left = sceneBoundingBox.left;
- top = sceneBoundingBox.top;
- w = sceneBoundingBox.right - sceneBoundingBox.left;
- h = sceneBoundingBox.bottom - sceneBoundingBox.top;
- } else {
- // otherwise just use whatever space we have
- var ownerRect = owner.getBoundingClientRect();
- w = ownerRect.width;
- h = ownerRect.height;
- }
- var bbox = panController.getBBox();
- if (bbox.width === 0 || bbox.height === 0) {
- // we probably do not have any elements in the SVG
- // just bail out;
- return;
- }
- var dh = h / bbox.height;
- var dw = w / bbox.width;
- var scale = Math.min(dw, dh);
- transform.x = -(bbox.left + bbox.width / 2) * scale + w / 2 + left;
- transform.y = -(bbox.top + bbox.height / 2) * scale + h / 2 + top;
- transform.scale = scale;
- }
- function getTransformModel() {
- // TODO: should this be read only?
- return transform;
- }
- function getMinZoom() {
- return minZoom;
- }
- function setMinZoom(newMinZoom) {
- minZoom = newMinZoom;
- }
- function getMaxZoom() {
- return maxZoom;
- }
- function setMaxZoom(newMaxZoom) {
- maxZoom = newMaxZoom;
- }
- function getTransformOrigin() {
- return transformOrigin;
- }
- function setTransformOrigin(newTransformOrigin) {
- transformOrigin = parseTransformOrigin(newTransformOrigin);
- }
- function getZoomSpeed() {
- return speed;
- }
- function setZoomSpeed(newSpeed) {
- if (!Number.isFinite(newSpeed)) {
- throw new Error('Zoom speed should be a number');
- }
- speed = newSpeed;
- }
- function getPoint() {
- return {
- x: transform.x,
- y: transform.y
- };
- }
- function moveTo(x, y) {
- transform.x = x;
- transform.y = y;
- keepTransformInsideBounds();
- triggerEvent('pan');
- makeDirty();
- }
- function moveBy(dx, dy) {
- moveTo(transform.x + dx, transform.y + dy);
- }
- function keepTransformInsideBounds() {
- var boundingBox = getBoundingBox();
- if (!boundingBox) return;
- var adjusted = false;
- var clientRect = getClientRect();
- var diff = boundingBox.left - clientRect.right;
- if (diff > 0) {
- transform.x += diff;
- adjusted = true;
- }
- // check the other side:
- diff = boundingBox.right - clientRect.left;
- if (diff < 0) {
- transform.x += diff;
- adjusted = true;
- }
- // y axis:
- diff = boundingBox.top - clientRect.bottom;
- if (diff > 0) {
- // we adjust transform, so that it matches exactly our bounding box:
- // transform.y = boundingBox.top - (boundingBox.height + boundingBox.y) * transform.scale =>
- // transform.y = boundingBox.top - (clientRect.bottom - transform.y) =>
- // transform.y = diff + transform.y =>
- transform.y += diff;
- adjusted = true;
- }
- diff = boundingBox.bottom - clientRect.top;
- if (diff < 0) {
- transform.y += diff;
- adjusted = true;
- }
- return adjusted;
- }
- /**
- * Returns bounding box that should be used to restrict scene movement.
- */
- function getBoundingBox() {
- if (!bounds) return; // client does not want to restrict movement
- if (typeof bounds === 'boolean') {
- // for boolean type we use parent container bounds
- var ownerRect = owner.getBoundingClientRect();
- var sceneWidth = ownerRect.width;
- var sceneHeight = ownerRect.height;
- return {
- left: sceneWidth * boundsPadding,
- top: sceneHeight * boundsPadding,
- right: sceneWidth * (1 - boundsPadding),
- bottom: sceneHeight * (1 - boundsPadding)
- };
- }
- return bounds;
- }
- function getClientRect() {
- var bbox = panController.getBBox();
- var leftTop = client(bbox.left, bbox.top);
- return {
- left: leftTop.x,
- top: leftTop.y,
- right: bbox.width * transform.scale + leftTop.x,
- bottom: bbox.height * transform.scale + leftTop.y
- };
- }
- function client(x, y) {
- return {
- x: x * transform.scale + transform.x,
- y: y * transform.scale + transform.y
- };
- }
- function makeDirty() {
- isDirty = true;
- frameAnimation = window.requestAnimationFrame(frame);
- }
- function zoomByRatio(clientX, clientY, ratio) {
- if (isNaN(clientX) || isNaN(clientY) || isNaN(ratio)) {
- throw new Error('zoom requires valid numbers');
- }
- var newScale = transform.scale * ratio;
- if (newScale < minZoom) {
- if (transform.scale === minZoom) return;
- ratio = minZoom / transform.scale;
- }
- if (newScale > maxZoom) {
- if (transform.scale === maxZoom) return;
- ratio = maxZoom / transform.scale;
- }
- var size = transformToScreen(clientX, clientY);
- transform.x = size.x - ratio * (size.x - transform.x);
- transform.y = size.y - ratio * (size.y - transform.y);
- // TODO: https://github.com/anvaka/panzoom/issues/112
- if (bounds && boundsPadding === 1 && minZoom === 1) {
- transform.scale *= ratio;
- keepTransformInsideBounds();
- } else {
- var transformAdjusted = keepTransformInsideBounds();
- if (!transformAdjusted) transform.scale *= ratio;
- }
- triggerEvent('zoom');
- makeDirty();
- }
- function zoomAbs(clientX, clientY, zoomLevel) {
- var ratio = zoomLevel / transform.scale;
- zoomByRatio(clientX, clientY, ratio);
- }
- function centerOn(ui) {
- var parent = ui.ownerSVGElement;
- if (!parent)
- throw new Error('ui element is required to be within the scene');
- // TODO: should i use controller's screen CTM?
- var clientRect = ui.getBoundingClientRect();
- var cx = clientRect.left + clientRect.width / 2;
- var cy = clientRect.top + clientRect.height / 2;
- var container = parent.getBoundingClientRect();
- var dx = container.width / 2 - cx;
- var dy = container.height / 2 - cy;
- internalMoveBy(dx, dy, true);
- }
- function internalMoveBy(dx, dy, smooth) {
- if (!smooth) {
- return moveBy(dx, dy);
- }
- if (moveByAnimation) moveByAnimation.cancel();
- var from = { x: 0, y: 0 };
- var to = { x: dx, y: dy };
- var lastX = 0;
- var lastY = 0;
- moveByAnimation = animate(from, to, {
- step: function (v) {
- moveBy(v.x - lastX, v.y - lastY);
- lastX = v.x;
- lastY = v.y;
- }
- });
- }
- function scroll(x, y) {
- cancelZoomAnimation();
- moveTo(x, y);
- }
- function dispose() {
- releaseEvents();
- }
- function listenForEvents() {
- owner.addEventListener('mousedown', onMouseDown, { passive: false });
- owner.addEventListener('dblclick', onDoubleClick, { passive: false });
- owner.addEventListener('touchstart', onTouch, { passive: false });
- owner.addEventListener('keydown', onKeyDown, { passive: false });
- // Need to listen on the owner container, so that we are not limited
- // by the size of the scrollable domElement
- wheel.addWheelListener(owner, onMouseWheel, { passive: false });
- makeDirty();
- }
- function releaseEvents() {
- wheel.removeWheelListener(owner, onMouseWheel);
- owner.removeEventListener('mousedown', onMouseDown);
- owner.removeEventListener('keydown', onKeyDown);
- owner.removeEventListener('dblclick', onDoubleClick);
- owner.removeEventListener('touchstart', onTouch);
- if (frameAnimation) {
- window.cancelAnimationFrame(frameAnimation);
- frameAnimation = 0;
- }
- smoothScroll.cancel();
- releaseDocumentMouse();
- releaseTouches();
- textSelection.release();
- triggerPanEnd();
- }
- function frame() {
- if (isDirty) applyTransform();
- }
- function applyTransform() {
- isDirty = false;
- // TODO: Should I allow to cancel this?
- panController.applyTransform(transform);
- triggerEvent('transform');
- frameAnimation = 0;
- }
- function onKeyDown(e) {
- var x = 0,
- y = 0,
- z = 0;
- if (e.keyCode === 38) {
- y = 1; // up
- } else if (e.keyCode === 40) {
- y = -1; // down
- } else if (e.keyCode === 37) {
- x = 1; // left
- } else if (e.keyCode === 39) {
- x = -1; // right
- } else if (e.keyCode === 189 || e.keyCode === 109) {
- // DASH or SUBTRACT
- z = 1; // `-` - zoom out
- } else if (e.keyCode === 187 || e.keyCode === 107) {
- // EQUAL SIGN or ADD
- z = -1; // `=` - zoom in (equal sign on US layout is under `+`)
- }
- if (filterKey(e, x, y, z)) {
- // They don't want us to handle the key: https://github.com/anvaka/panzoom/issues/45
- return;
- }
- if (x || y) {
- e.preventDefault();
- e.stopPropagation();
- var clientRect = owner.getBoundingClientRect();
- // movement speed should be the same in both X and Y direction:
- var offset = Math.min(clientRect.width, clientRect.height);
- var moveSpeedRatio = 0.05;
- var dx = offset * moveSpeedRatio * x;
- var dy = offset * moveSpeedRatio * y;
- // TODO: currently we do not animate this. It could be better to have animation
- internalMoveBy(dx, dy);
- }
- if (z) {
- var scaleMultiplier = getScaleMultiplier(z * 100);
- var offset = transformOrigin ? getTransformOriginOffset() : midPoint();
- publicZoomTo(offset.x, offset.y, scaleMultiplier);
- }
- }
- function midPoint() {
- var ownerRect = owner.getBoundingClientRect();
- return {
- x: ownerRect.width / 2,
- y: ownerRect.height / 2
- };
- }
- function onTouch(e) {
- // let the override the touch behavior
- beforeTouch(e);
- if (e.touches.length === 1) {
- return handleSingleFingerTouch(e, e.touches[0]);
- } else if (e.touches.length === 2) {
- // handleTouchMove() will care about pinch zoom.
- pinchZoomLength = getPinchZoomLength(e.touches[0], e.touches[1]);
- multiTouch = true;
- startTouchListenerIfNeeded();
- }
- }
- function beforeTouch(e) {
- // TODO: Need to unify this filtering names. E.g. use `beforeTouch`
- if (options.onTouch && !options.onTouch(e)) {
- // if they return `false` from onTouch, we don't want to stop
- // events propagation. Fixes https://github.com/anvaka/panzoom/issues/12
- return;
- }
- e.stopPropagation();
- e.preventDefault();
- }
- function beforeDoubleClick(e) {
- // TODO: Need to unify this filtering names. E.g. use `beforeDoubleClick``
- if (options.onDoubleClick && !options.onDoubleClick(e)) {
- // if they return `false` from onTouch, we don't want to stop
- // events propagation. Fixes https://github.com/anvaka/panzoom/issues/46
- return;
- }
- e.preventDefault();
- e.stopPropagation();
- }
- function handleSingleFingerTouch(e) {
- var touch = e.touches[0];
- var offset = getOffsetXY(touch);
- lastSingleFingerOffset = offset;
- var point = transformToScreen(offset.x, offset.y);
- mouseX = point.x;
- mouseY = point.y;
- smoothScroll.cancel();
- startTouchListenerIfNeeded();
- }
- function startTouchListenerIfNeeded() {
- if (touchInProgress) {
- // no need to do anything, as we already listen to events;
- return;
- }
- touchInProgress = true;
- document.addEventListener('touchmove', handleTouchMove);
- document.addEventListener('touchend', handleTouchEnd);
- document.addEventListener('touchcancel', handleTouchEnd);
- }
- function handleTouchMove(e) {
- if (e.touches.length === 1) {
- e.stopPropagation();
- var touch = e.touches[0];
- var offset = getOffsetXY(touch);
- var point = transformToScreen(offset.x, offset.y);
- var dx = point.x - mouseX;
- var dy = point.y - mouseY;
- if (dx !== 0 && dy !== 0) {
- triggerPanStart();
- }
- mouseX = point.x;
- mouseY = point.y;
- internalMoveBy(dx, dy);
- } else if (e.touches.length === 2) {
- // it's a zoom, let's find direction
- multiTouch = true;
- var t1 = e.touches[0];
- var t2 = e.touches[1];
- var currentPinchLength = getPinchZoomLength(t1, t2);
- // since the zoom speed is always based on distance from 1, we need to apply
- // pinch speed only on that distance from 1:
- var scaleMultiplier =
- 1 + (currentPinchLength / pinchZoomLength - 1) * pinchSpeed;
- var firstTouchPoint = getOffsetXY(t1);
- var secondTouchPoint = getOffsetXY(t2);
- mouseX = (firstTouchPoint.x + secondTouchPoint.x) / 2;
- mouseY = (firstTouchPoint.y + secondTouchPoint.y) / 2;
- if (transformOrigin) {
- var offset = getTransformOriginOffset();
- mouseX = offset.x;
- mouseY = offset.y;
- }
- publicZoomTo(mouseX, mouseY, scaleMultiplier);
- pinchZoomLength = currentPinchLength;
- e.stopPropagation();
- e.preventDefault();
- }
- }
- function handleTouchEnd(e) {
- if (e.touches.length > 0) {
- var offset = getOffsetXY(e.touches[0]);
- var point = transformToScreen(offset.x, offset.y);
- mouseX = point.x;
- mouseY = point.y;
- } else {
- var now = new Date();
- if (now - lastTouchEndTime < doubleTapSpeedInMS) {
- if (transformOrigin) {
- var offset = getTransformOriginOffset();
- smoothZoom(offset.x, offset.y, zoomDoubleClickSpeed);
- } else {
- smoothZoom(lastSingleFingerOffset.x, lastSingleFingerOffset.y, zoomDoubleClickSpeed);
- }
- }
- lastTouchEndTime = now;
- touchInProgress = false;
- triggerPanEnd();
- releaseTouches();
- }
- }
- function getPinchZoomLength(finger1, finger2) {
- var dx = finger1.clientX - finger2.clientX;
- var dy = finger1.clientY - finger2.clientY;
- return Math.sqrt(dx * dx + dy * dy);
- }
- function onDoubleClick(e) {
- beforeDoubleClick(e);
- var offset = getOffsetXY(e);
- if (transformOrigin) {
- // TODO: looks like this is duplicated in the file.
- // Need to refactor
- offset = getTransformOriginOffset();
- }
- smoothZoom(offset.x, offset.y, zoomDoubleClickSpeed);
- }
- function onMouseDown(e) {
- // if client does not want to handle this event - just ignore the call
- if (beforeMouseDown(e)) return;
- if (touchInProgress) {
- // modern browsers will fire mousedown for touch events too
- // we do not want this: touch is handled separately.
- e.stopPropagation();
- return false;
- }
- // for IE, left click == 1
- // for Firefox, left click == 0
- var isLeftButton =
- (e.button === 1 && window.event !== null) || e.button === 0;
- if (!isLeftButton) return;
- smoothScroll.cancel();
- var offset = getOffsetXY(e);
- var point = transformToScreen(offset.x, offset.y);
- mouseX = point.x;
- mouseY = point.y;
- // We need to listen on document itself, since mouse can go outside of the
- // window, and we will loose it
- document.addEventListener('mousemove', onMouseMove);
- document.addEventListener('mouseup', onMouseUp);
- textSelection.capture(e.target || e.srcElement);
- return false;
- }
- function onMouseMove(e) {
- // no need to worry about mouse events when touch is happening
- if (touchInProgress) return;
- triggerPanStart();
- var offset = getOffsetXY(e);
- var point = transformToScreen(offset.x, offset.y);
- var dx = point.x - mouseX;
- var dy = point.y - mouseY;
- mouseX = point.x;
- mouseY = point.y;
- internalMoveBy(dx, dy);
- }
- function onMouseUp() {
- textSelection.release();
- triggerPanEnd();
- releaseDocumentMouse();
- }
- function releaseDocumentMouse() {
- document.removeEventListener('mousemove', onMouseMove);
- document.removeEventListener('mouseup', onMouseUp);
- panstartFired = false;
- }
- function releaseTouches() {
- document.removeEventListener('touchmove', handleTouchMove);
- document.removeEventListener('touchend', handleTouchEnd);
- document.removeEventListener('touchcancel', handleTouchEnd);
- panstartFired = false;
- multiTouch = false;
- }
- function onMouseWheel(e) {
- // if client does not want to handle this event - just ignore the call
- if (beforeWheel(e)) return;
- smoothScroll.cancel();
- var delta = e.deltaY;
- if (e.deltaMode > 0) delta *= 100;
- var scaleMultiplier = getScaleMultiplier(delta);
- if (scaleMultiplier !== 1) {
- var offset = transformOrigin
- ? getTransformOriginOffset()
- : getOffsetXY(e);
- publicZoomTo(offset.x, offset.y, scaleMultiplier);
- e.preventDefault();
- }
- }
- function getOffsetXY(e) {
- var offsetX, offsetY;
- // I tried using e.offsetX, but that gives wrong results for svg, when user clicks on a path.
- var ownerRect = owner.getBoundingClientRect();
- offsetX = e.clientX - ownerRect.left;
- offsetY = e.clientY - ownerRect.top;
- return { x: offsetX, y: offsetY };
- }
- function smoothZoom(clientX, clientY, scaleMultiplier) {
- var fromValue = transform.scale;
- var from = { scale: fromValue };
- var to = { scale: scaleMultiplier * fromValue };
- smoothScroll.cancel();
- cancelZoomAnimation();
- zoomToAnimation = animate(from, to, {
- step: function (v) {
- zoomAbs(clientX, clientY, v.scale);
- },
- done: triggerZoomEnd
- });
- }
- function smoothZoomAbs(clientX, clientY, toScaleValue) {
- var fromValue = transform.scale;
- var from = { scale: fromValue };
- var to = { scale: toScaleValue };
- smoothScroll.cancel();
- cancelZoomAnimation();
- zoomToAnimation = animate(from, to, {
- step: function (v) {
- zoomAbs(clientX, clientY, v.scale);
- }
- });
- }
- function getTransformOriginOffset() {
- var ownerRect = owner.getBoundingClientRect();
- return {
- x: ownerRect.width * transformOrigin.x,
- y: ownerRect.height * transformOrigin.y
- };
- }
- function publicZoomTo(clientX, clientY, scaleMultiplier) {
- smoothScroll.cancel();
- cancelZoomAnimation();
- return zoomByRatio(clientX, clientY, scaleMultiplier);
- }
- function cancelZoomAnimation() {
- if (zoomToAnimation) {
- zoomToAnimation.cancel();
- zoomToAnimation = null;
- }
- }
- function getScaleMultiplier(delta) {
- var sign = Math.sign(delta);
- var deltaAdjustedSpeed = Math.min(0.25, Math.abs(speed * delta / 128));
- return 1 - sign * deltaAdjustedSpeed;
- }
- function triggerPanStart() {
- if (!panstartFired) {
- triggerEvent('panstart');
- panstartFired = true;
- smoothScroll.start();
- }
- }
- function triggerPanEnd() {
- if (panstartFired) {
- // we should never run smooth scrolling if it was multiTouch (pinch zoom animation):
- if (!multiTouch) smoothScroll.stop();
- triggerEvent('panend');
- }
- }
- function triggerZoomEnd() {
- triggerEvent('zoomend');
- }
- function triggerEvent(name) {
- api.fire(name, api);
- }
- }
- function parseTransformOrigin(options) {
- if (!options) return;
- if (typeof options === 'object') {
- if (!isNumber(options.x) || !isNumber(options.y))
- failTransformOrigin(options);
- return options;
- }
- failTransformOrigin();
- }
- function failTransformOrigin(options) {
- console.error(options);
- throw new Error(
- [
- 'Cannot parse transform origin.',
- 'Some good examples:',
- ' "center center" can be achieved with {x: 0.5, y: 0.5}',
- ' "top center" can be achieved with {x: 0.5, y: 0}',
- ' "bottom right" can be achieved with {x: 1, y: 1}'
- ].join('\n')
- );
- }
- function noop() { }
- function validateBounds(bounds) {
- var boundsType = typeof bounds;
- if (boundsType === 'undefined' || boundsType === 'boolean') return; // this is okay
- // otherwise need to be more thorough:
- var validBounds =
- isNumber(bounds.left) &&
- isNumber(bounds.top) &&
- isNumber(bounds.bottom) &&
- isNumber(bounds.right);
- if (!validBounds)
- throw new Error(
- 'Bounds object is not valid. It can be: ' +
- 'undefined, boolean (true|false) or an object {left, top, right, bottom}'
- );
- }
- function isNumber(x) {
- return Number.isFinite(x);
- }
- // IE 11 does not support isNaN:
- function isNaN(value) {
- if (Number.isNaN) {
- return Number.isNaN(value);
- }
- return value !== value;
- }
- function rigidScroll() {
- return {
- start: noop,
- stop: noop,
- cancel: noop
- };
- }
- function autoRun() {
- if (typeof document === 'undefined') return;
- var scripts = document.getElementsByTagName('script');
- if (!scripts) return;
- var panzoomScript;
- for (var i = 0; i < scripts.length; ++i) {
- var x = scripts[i];
- if (x.src && x.src.match(/\bpanzoom(\.min)?\.js/)) {
- panzoomScript = x;
- break;
- }
- }
- if (!panzoomScript) return;
- var query = panzoomScript.getAttribute('query');
- if (!query) return;
- var globalName = panzoomScript.getAttribute('name') || 'pz';
- var started = Date.now();
- tryAttach();
- function tryAttach() {
- var el = document.querySelector(query);
- if (!el) {
- var now = Date.now();
- var elapsed = now - started;
- if (elapsed < 2000) {
- // Let's wait a bit
- setTimeout(tryAttach, 100);
- return;
- }
- // If we don't attach within 2 seconds to the target element, consider it a failure
- console.error('Cannot find the panzoom element', globalName);
- return;
- }
- var options = collectOptions(panzoomScript);
- console.log(options);
- window[globalName] = createPanZoom(el, options);
- }
- function collectOptions(script) {
- var attrs = script.attributes;
- var options = {};
- for (var i = 0; i < attrs.length; ++i) {
- var attr = attrs[i];
- var nameValue = getPanzoomAttributeNameValue(attr);
- if (nameValue) {
- options[nameValue.name] = nameValue.value;
- }
- }
- return options;
- }
- function getPanzoomAttributeNameValue(attr) {
- if (!attr.name) return;
- var isPanZoomAttribute =
- attr.name[0] === 'p' && attr.name[1] === 'z' && attr.name[2] === '-';
- if (!isPanZoomAttribute) return;
- var name = attr.name.substr(3);
- var value = JSON.parse(attr.value);
- return { name: name, value: value };
- }
- }
- autoRun();
- },{"./lib/createTextSelectionInterceptor.js":2,"./lib/domController.js":3,"./lib/kinetic.js":4,"./lib/svgController.js":5,"./lib/transform.js":6,"amator":7,"ngraph.events":9,"wheel":10}],2:[function(require,module,exports){
- /**
- * Disallows selecting text.
- */
- module.exports = createTextSelectionInterceptor;
- function createTextSelectionInterceptor(useFake) {
- if (useFake) {
- return {
- capture: noop,
- release: noop
- };
- }
- var dragObject;
- var prevSelectStart;
- var prevDragStart;
- var wasCaptured = false;
- return {
- capture: capture,
- release: release
- };
- function capture(domObject) {
- wasCaptured = true;
- prevSelectStart = window.document.onselectstart;
- prevDragStart = window.document.ondragstart;
- window.document.onselectstart = disabled;
- dragObject = domObject;
- dragObject.ondragstart = disabled;
- }
- function release() {
- if (!wasCaptured) return;
-
- wasCaptured = false;
- window.document.onselectstart = prevSelectStart;
- if (dragObject) dragObject.ondragstart = prevDragStart;
- }
- }
- function disabled(e) {
- e.stopPropagation();
- return false;
- }
- function noop() {}
- },{}],3:[function(require,module,exports){
- module.exports = makeDomController
- function makeDomController(domElement, options) {
- var elementValid = (domElement instanceof HTMLElement)
- if (!elementValid) {
- throw new Error('svg element is required for svg.panzoom to work')
- }
- var owner = domElement.parentElement
- if (!owner) {
- throw new Error(
- 'Do not apply panzoom to the detached DOM element. '
- )
- }
- domElement.scrollTop = 0;
-
- if (!options.disableKeyboardInteraction) {
- owner.setAttribute('tabindex', 0);
- }
- var api = {
- getBBox: getBBox,
- getOwner: getOwner,
- applyTransform: applyTransform,
- }
-
- return api
- function getOwner() {
- return owner
- }
- function getBBox() {
- // TODO: We should probably cache this?
- return {
- left: 0,
- top: 0,
- width: domElement.clientWidth,
- height: domElement.clientHeight
- }
- }
- function applyTransform(transform) {
- // TODO: Should we cache this?
- domElement.style.transformOrigin = '0 0 0';
- domElement.style.transform = 'matrix(' +
- transform.scale + ', 0, 0, ' +
- transform.scale + ', ' +
- transform.x + ', ' + transform.y + ')'
- }
- }
- },{}],4:[function(require,module,exports){
- (function (global){
- /**
- * Allows smooth kinetic scrolling of the surface
- */
- module.exports = kinetic;
- function kinetic(getPoint, scroll, settings) {
- if (typeof settings !== 'object') {
- // setting could come as boolean, we should ignore it, and use an object.
- settings = {};
- }
- var minVelocity = typeof settings.minVelocity === 'number' ? settings.minVelocity : 5;
- var amplitude = typeof settings.amplitude === 'number' ? settings.amplitude : 0.25;
- var cancelAnimationFrame = typeof settings.cancelAnimationFrame === 'function' ? settings.cancelAnimationFrame : getCancelAnimationFrame();
- var requestAnimationFrame = typeof settings.requestAnimationFrame === 'function' ? settings.requestAnimationFrame : getRequestAnimationFrame();
- var lastPoint;
- var timestamp;
- var timeConstant = 342;
- var ticker;
- var vx, targetX, ax;
- var vy, targetY, ay;
- var raf;
- return {
- start: start,
- stop: stop,
- cancel: dispose
- };
- function dispose() {
- cancelAnimationFrame(ticker);
- cancelAnimationFrame(raf);
- }
- function start() {
- lastPoint = getPoint();
- ax = ay = vx = vy = 0;
- timestamp = new Date();
- cancelAnimationFrame(ticker);
- cancelAnimationFrame(raf);
- // we start polling the point position to accumulate velocity
- // Once we stop(), we will use accumulated velocity to keep scrolling
- // an object.
- ticker = requestAnimationFrame(track);
- }
- function track() {
- var now = Date.now();
- var elapsed = now - timestamp;
- timestamp = now;
- var currentPoint = getPoint();
- var dx = currentPoint.x - lastPoint.x;
- var dy = currentPoint.y - lastPoint.y;
- lastPoint = currentPoint;
- var dt = 1000 / (1 + elapsed);
- // moving average
- vx = 0.8 * dx * dt + 0.2 * vx;
- vy = 0.8 * dy * dt + 0.2 * vy;
- ticker = requestAnimationFrame(track);
- }
- function stop() {
- cancelAnimationFrame(ticker);
- cancelAnimationFrame(raf);
- var currentPoint = getPoint();
- targetX = currentPoint.x;
- targetY = currentPoint.y;
- timestamp = Date.now();
- if (vx < -minVelocity || vx > minVelocity) {
- ax = amplitude * vx;
- targetX += ax;
- }
- if (vy < -minVelocity || vy > minVelocity) {
- ay = amplitude * vy;
- targetY += ay;
- }
- raf = requestAnimationFrame(autoScroll);
- }
- function autoScroll() {
- var elapsed = Date.now() - timestamp;
- var moving = false;
- var dx = 0;
- var dy = 0;
- if (ax) {
- dx = -ax * Math.exp(-elapsed / timeConstant);
- if (dx > 0.5 || dx < -0.5) moving = true;
- else dx = ax = 0;
- }
- if (ay) {
- dy = -ay * Math.exp(-elapsed / timeConstant);
- if (dy > 0.5 || dy < -0.5) moving = true;
- else dy = ay = 0;
- }
- if (moving) {
- scroll(targetX + dx, targetY + dy);
- raf = requestAnimationFrame(autoScroll);
- }
- }
- }
- function getCancelAnimationFrame() {
- if (typeof global.cancelAnimationFrame === 'function') return global.cancelAnimationFrame;
- return clearTimeout;
- }
- function getRequestAnimationFrame() {
- if (typeof global.requestAnimationFrame === 'function') return global.requestAnimationFrame;
- return function (handler) {
- return setTimeout(handler, 16);
- }
- }
- }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
- },{}],5:[function(require,module,exports){
- module.exports = makeSvgController
- function makeSvgController(svgElement, options) {
- var elementValid = (svgElement instanceof SVGElement)
- if (!elementValid) {
- throw new Error('svg element is required for svg.panzoom to work')
- }
- var owner = svgElement.ownerSVGElement
- if (!owner) {
- throw new Error(
- 'Do not apply panzoom to the root <svg> element. ' +
- 'Use its child instead (e.g. <g></g>). ' +
- 'As of March 2016 only FireFox supported transform on the root element')
- }
- if (!options.disableKeyboardInteraction) {
- owner.setAttribute('tabindex', 0);
- }
- var api = {
- getBBox: getBBox,
- getScreenCTM: getScreenCTM,
- getOwner: getOwner,
- applyTransform: applyTransform,
- initTransform: initTransform
- }
-
- return api
- function getOwner() {
- return owner
- }
- function getBBox() {
- var bbox = svgElement.getBBox()
- return {
- left: bbox.x,
- top: bbox.y,
- width: bbox.width,
- height: bbox.height,
- }
- }
- function getScreenCTM() {
- var ctm = owner.getCTM();
- if (!ctm) {
- // This is likely firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=873106
- // The code below is not entirely correct, but still better than nothing
- return owner.getScreenCTM();
- }
- return ctm;
- }
- function initTransform(transform) {
- var screenCTM = svgElement.getCTM()
- transform.x = screenCTM.e;
- transform.y = screenCTM.f;
- transform.scale = screenCTM.a;
- owner.removeAttributeNS(null, 'viewBox');
- }
- function applyTransform(transform) {
- svgElement.setAttribute('transform', 'matrix(' +
- transform.scale + ' 0 0 ' +
- transform.scale + ' ' +
- transform.x + ' ' + transform.y + ')')
- }
- }
- },{}],6:[function(require,module,exports){
- module.exports = Transform;
- function Transform() {
- this.x = 0;
- this.y = 0;
- this.scale = 1;
- }
- },{}],7:[function(require,module,exports){
- var BezierEasing = require('bezier-easing')
- // Predefined set of animations. Similar to CSS easing functions
- var animations = {
- ease: BezierEasing(0.25, 0.1, 0.25, 1),
- easeIn: BezierEasing(0.42, 0, 1, 1),
- easeOut: BezierEasing(0, 0, 0.58, 1),
- easeInOut: BezierEasing(0.42, 0, 0.58, 1),
- linear: BezierEasing(0, 0, 1, 1)
- }
- module.exports = animate;
- module.exports.makeAggregateRaf = makeAggregateRaf;
- module.exports.sharedScheduler = makeAggregateRaf();
- function animate(source, target, options) {
- var start = Object.create(null)
- var diff = Object.create(null)
- options = options || {}
- // We let clients specify their own easing function
- var easing = (typeof options.easing === 'function') ? options.easing : animations[options.easing]
- // if nothing is specified, default to ease (similar to CSS animations)
- if (!easing) {
- if (options.easing) {
- console.warn('Unknown easing function in amator: ' + options.easing);
- }
- easing = animations.ease
- }
- var step = typeof options.step === 'function' ? options.step : noop
- var done = typeof options.done === 'function' ? options.done : noop
- var scheduler = getScheduler(options.scheduler)
- var keys = Object.keys(target)
- keys.forEach(function(key) {
- start[key] = source[key]
- diff[key] = target[key] - source[key]
- })
- var durationInMs = typeof options.duration === 'number' ? options.duration : 400
- var durationInFrames = Math.max(1, durationInMs * 0.06) // 0.06 because 60 frames pers 1,000 ms
- var previousAnimationId
- var frame = 0
- previousAnimationId = scheduler.next(loop)
- return {
- cancel: cancel
- }
- function cancel() {
- scheduler.cancel(previousAnimationId)
- previousAnimationId = 0
- }
- function loop() {
- var t = easing(frame/durationInFrames)
- frame += 1
- setValues(t)
- if (frame <= durationInFrames) {
- previousAnimationId = scheduler.next(loop)
- step(source)
- } else {
- previousAnimationId = 0
- setTimeout(function() { done(source) }, 0)
- }
- }
- function setValues(t) {
- keys.forEach(function(key) {
- source[key] = diff[key] * t + start[key]
- })
- }
- }
- function noop() { }
- function getScheduler(scheduler) {
- if (!scheduler) {
- var canRaf = typeof window !== 'undefined' && window.requestAnimationFrame
- return canRaf ? rafScheduler() : timeoutScheduler()
- }
- if (typeof scheduler.next !== 'function') throw new Error('Scheduler is supposed to have next(cb) function')
- if (typeof scheduler.cancel !== 'function') throw new Error('Scheduler is supposed to have cancel(handle) function')
- return scheduler
- }
- function rafScheduler() {
- return {
- next: window.requestAnimationFrame.bind(window),
- cancel: window.cancelAnimationFrame.bind(window)
- }
- }
- function timeoutScheduler() {
- return {
- next: function(cb) {
- return setTimeout(cb, 1000/60)
- },
- cancel: function (id) {
- return clearTimeout(id)
- }
- }
- }
- function makeAggregateRaf() {
- var frontBuffer = new Set();
- var backBuffer = new Set();
- var frameToken = 0;
- return {
- next: next,
- cancel: next,
- clearAll: clearAll
- }
- function clearAll() {
- frontBuffer.clear();
- backBuffer.clear();
- cancelAnimationFrame(frameToken);
- frameToken = 0;
- }
- function next(callback) {
- backBuffer.add(callback);
- renderNextFrame();
- }
- function renderNextFrame() {
- if (!frameToken) frameToken = requestAnimationFrame(renderFrame);
- }
- function renderFrame() {
- frameToken = 0;
- var t = backBuffer;
- backBuffer = frontBuffer;
- frontBuffer = t;
- frontBuffer.forEach(function(callback) {
- callback();
- });
- frontBuffer.clear();
- }
- function cancel(callback) {
- backBuffer.delete(callback);
- }
- }
- },{"bezier-easing":8}],8:[function(require,module,exports){
- /**
- * https://github.com/gre/bezier-easing
- * BezierEasing - use bezier curve for transition easing function
- * by Gaëtan Renaudeau 2014 - 2015 – MIT License
- */
- // These values are established by empiricism with tests (tradeoff: performance VS precision)
- var NEWTON_ITERATIONS = 4;
- var NEWTON_MIN_SLOPE = 0.001;
- var SUBDIVISION_PRECISION = 0.0000001;
- var SUBDIVISION_MAX_ITERATIONS = 10;
- var kSplineTableSize = 11;
- var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
- var float32ArraySupported = typeof Float32Array === 'function';
- function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
- function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
- function C (aA1) { return 3.0 * aA1; }
- // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
- function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; }
- // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
- function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); }
- function binarySubdivide (aX, aA, aB, mX1, mX2) {
- var currentX, currentT, i = 0;
- do {
- currentT = aA + (aB - aA) / 2.0;
- currentX = calcBezier(currentT, mX1, mX2) - aX;
- if (currentX > 0.0) {
- aB = currentT;
- } else {
- aA = currentT;
- }
- } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
- return currentT;
- }
- function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
- for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
- var currentSlope = getSlope(aGuessT, mX1, mX2);
- if (currentSlope === 0.0) {
- return aGuessT;
- }
- var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
- aGuessT -= currentX / currentSlope;
- }
- return aGuessT;
- }
- function LinearEasing (x) {
- return x;
- }
- module.exports = function bezier (mX1, mY1, mX2, mY2) {
- if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
- throw new Error('bezier x values must be in [0, 1] range');
- }
- if (mX1 === mY1 && mX2 === mY2) {
- return LinearEasing;
- }
- // Precompute samples table
- var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
- for (var i = 0; i < kSplineTableSize; ++i) {
- sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
- }
- function getTForX (aX) {
- var intervalStart = 0.0;
- var currentSample = 1;
- var lastSample = kSplineTableSize - 1;
- for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
- intervalStart += kSampleStepSize;
- }
- --currentSample;
- // Interpolate to provide an initial guess for t
- var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
- var guessForT = intervalStart + dist * kSampleStepSize;
- var initialSlope = getSlope(guessForT, mX1, mX2);
- if (initialSlope >= NEWTON_MIN_SLOPE) {
- return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
- } else if (initialSlope === 0.0) {
- return guessForT;
- } else {
- return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
- }
- }
- return function BezierEasing (x) {
- // Because JavaScript number are imprecise, we should guarantee the extremes are right.
- if (x === 0) {
- return 0;
- }
- if (x === 1) {
- return 1;
- }
- return calcBezier(getTForX(x), mY1, mY2);
- };
- };
- },{}],9:[function(require,module,exports){
- module.exports = function(subject) {
- validateSubject(subject);
- var eventsStorage = createEventsStorage(subject);
- subject.on = eventsStorage.on;
- subject.off = eventsStorage.off;
- subject.fire = eventsStorage.fire;
- return subject;
- };
- function createEventsStorage(subject) {
- // Store all event listeners to this hash. Key is event name, value is array
- // of callback records.
- //
- // A callback record consists of callback function and its optional context:
- // { 'eventName' => [{callback: function, ctx: object}] }
- var registeredEvents = Object.create(null);
- return {
- on: function (eventName, callback, ctx) {
- if (typeof callback !== 'function') {
- throw new Error('callback is expected to be a function');
- }
- var handlers = registeredEvents[eventName];
- if (!handlers) {
- handlers = registeredEvents[eventName] = [];
- }
- handlers.push({callback: callback, ctx: ctx});
- return subject;
- },
- off: function (eventName, callback) {
- var wantToRemoveAll = (typeof eventName === 'undefined');
- if (wantToRemoveAll) {
- // Killing old events storage should be enough in this case:
- registeredEvents = Object.create(null);
- return subject;
- }
- if (registeredEvents[eventName]) {
- var deleteAllCallbacksForEvent = (typeof callback !== 'function');
- if (deleteAllCallbacksForEvent) {
- delete registeredEvents[eventName];
- } else {
- var callbacks = registeredEvents[eventName];
- for (var i = 0; i < callbacks.length; ++i) {
- if (callbacks[i].callback === callback) {
- callbacks.splice(i, 1);
- }
- }
- }
- }
- return subject;
- },
- fire: function (eventName) {
- var callbacks = registeredEvents[eventName];
- if (!callbacks) {
- return subject;
- }
- var fireArguments;
- if (arguments.length > 1) {
- fireArguments = Array.prototype.splice.call(arguments, 1);
- }
- for(var i = 0; i < callbacks.length; ++i) {
- var callbackInfo = callbacks[i];
- callbackInfo.callback.apply(callbackInfo.ctx, fireArguments);
- }
- return subject;
- }
- };
- }
- function validateSubject(subject) {
- if (!subject) {
- throw new Error('Eventify cannot use falsy object as events subject');
- }
- var reservedWords = ['on', 'fire', 'off'];
- for (var i = 0; i < reservedWords.length; ++i) {
- if (subject.hasOwnProperty(reservedWords[i])) {
- throw new Error("Subject cannot be eventified, since it already has property '" + reservedWords[i] + "'");
- }
- }
- }
- },{}],10:[function(require,module,exports){
- /**
- * This module used to unify mouse wheel behavior between different browsers in 2014
- * Now it's just a wrapper around addEventListener('wheel');
- *
- * Usage:
- * var addWheelListener = require('wheel').addWheelListener;
- * var removeWheelListener = require('wheel').removeWheelListener;
- * addWheelListener(domElement, function (e) {
- * // mouse wheel event
- * });
- * removeWheelListener(domElement, function);
- */
- module.exports = addWheelListener;
- // But also expose "advanced" api with unsubscribe:
- module.exports.addWheelListener = addWheelListener;
- module.exports.removeWheelListener = removeWheelListener;
- function addWheelListener(element, listener, useCapture) {
- element.addEventListener('wheel', listener, useCapture);
- }
- function removeWheelListener( element, listener, useCapture ) {
- element.removeEventListener('wheel', listener, useCapture);
- }
- },{}]},{},[1])(1)
- });
|