(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o199&&b<300){r(c);}else{t(c);}};a.open("GET",u,true);a.send(null);})} // npm modules window.EventEmitter = require("event-emitter-es6"); window.FaviconNotification = require("favicon-notification"); window.panzoom = require("pan-zoom"); // Our files class BoardWindow extends EventEmitter { constructor(canvas) { super(); // Run the parent constructor // The maximum target fps. this.maxFps = 60; // The target fps at which we should send sursor updates. this.cursorUpdateFrequency = 5; // Setup the fps indicator in the top right corner this.renderTimeIndicator = document.createElement("span"); this.renderTimeIndicator.innerHTML = "0ms"; document.querySelector(".fps").appendChild(this.renderTimeIndicator); // Setup the canvas this.canvas = canvas; this.context = canvas.getContext("2d"); // --~~~-- // Setup the favicon thingy FaviconNotification.init({ color: '#ff6333' }); FaviconNotification.add(); // Setup the input controls window.panzoom(canvas, this.handleCanvasMovement.bind(this)); // Fetch the RippleLink connection information and other settings from // the server get("/Settings.json").then(JSON.parse).then((function(settings) { console.info("[setup]", "Obtained settings from server:", settings); this.settings = settings; this.setup(); }).bind(this), function(errorMessage) { console.error(`Error: Failed to fetch settings from server! Response: ${errorMessage}`); }); // Make the canvas track the window size this.trackWindowSize(); } /** * Setup ready for user input. * This mainly consists of establishing the RippleLink connection to the server. */ setup() { this.rippleLink = new RippleLink(this.settings.WebsocketUri, this); this.rippleLink.on("connect", (function(event) { // Send the handshake request this.rippleLink.send({ event: "HandshakeRequest", InitialViewport: { // TODO: Add support for persisting this between sessions X: 0, Y: 0, Width: window.innerWidth, Height: window.innerHeight }, InitialAbsCursorPosition: this.cursorPosition }); }).bind(this)); // Track the mouse position this.cursorSyncer = new CursorSyncer(this.rippleLink, this.cursorUpdateFrequency); // RippleLink message bindings } /** * Renders the next frame. */ nextFrame() { // The time at which the current frame started rendering. let frameStart = +new Date(); if(frameStart - this.lastFrameStart >= (1 / this.maxFps) * 1000) { this.update(); this.render(this.canvas, this.context); } // Update the time the last frame started rendering this.lastFrameStart = frameStart; // Update the time we took rendering the last frame this.lastFrameTime = +new Date() - frameStart; this.renderTimeIndicator.innerHTML = `${this.lastFrameTime}ms`; // Limit the maximum fps requestAnimationFrame(this.nextFrame.bind(this)); } /** * Updates everything ready for the next frame to be rendered. */ update() { } /** * Renders the next frame. */ render(canvas, context) { context.clearRect(0, 0, canvas.width, canvas.height); context.fillStyle = "red"; context.fillRect(10, 10, 100, 100); } /** * Updates the canvas size to match the current viewport size. */ matchWindowSize() { this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; this.render(this.canvas, this.context); } /** * Makes the canvas size track the window size. */ trackWindowSize() { this.matchWindowSize(); window.addEventListener("resize", this.matchWindowSize.bind(this)); } /** * Handles events generated by pan-zoom, the package that handles the * dragging and zooming of the whiteboard. */ handleCanvasMovement(event) { this.viewportState = event; // Store the viewport information for later } } window.FpsIndicator = require("fps-indicator"); window.addEventListener("load", function (event) { let fpsIndicator = FpsIndicator({ updatePeriod: 1000, maxFps: 60 }); fpsIndicator.element.style.color = "rgb(114, 194, 179)"; let canvas = document.getElementById("canvas-main"), boardWindow = new BoardWindow(canvas); boardWindow.nextFrame(); window.boardWindow = boardWindow; }); },{"event-emitter-es6":3,"favicon-notification":4,"fps-indicator":5,"pan-zoom":10}],2:[function(require,module,exports){ module.exports = defaultProperty function defaultProperty (get, set) { return { configurable: true, enumerable: true, get: get, set: set } } },{}],3:[function(require,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var DEFAULT_VALUES = { emitDelay: 10, strictMode: false }; /** * @typedef {object} EventEmitterListenerFunc * @property {boolean} once * @property {function} fn */ /** * @class EventEmitter * * @private * @property {Object.} _listeners * @property {string[]} events */ var EventEmitter = function () { /** * @constructor * @param {{}} [opts] * @param {number} [opts.emitDelay = 10] - Number in ms. Specifies whether emit will be sync or async. By default - 10ms. If 0 - fires sync * @param {boolean} [opts.strictMode = false] - is true, Emitter throws error on emit error with no listeners */ function EventEmitter() { var opts = arguments.length <= 0 || arguments[0] === undefined ? DEFAULT_VALUES : arguments[0]; _classCallCheck(this, EventEmitter); var emitDelay = void 0, strictMode = void 0; if (opts.hasOwnProperty('emitDelay')) { emitDelay = opts.emitDelay; } else { emitDelay = DEFAULT_VALUES.emitDelay; } this._emitDelay = emitDelay; if (opts.hasOwnProperty('strictMode')) { strictMode = opts.strictMode; } else { strictMode = DEFAULT_VALUES.strictMode; } this._strictMode = strictMode; this._listeners = {}; this.events = []; } /** * @protected * @param {string} type * @param {function} listener * @param {boolean} [once = false] */ _createClass(EventEmitter, [{ key: '_addListenner', value: function _addListenner(type, listener, once) { if (typeof listener !== 'function') { throw TypeError('listener must be a function'); } if (this.events.indexOf(type) === -1) { this._listeners[type] = [{ once: once, fn: listener }]; this.events.push(type); } else { this._listeners[type].push({ once: once, fn: listener }); } } /** * Subscribes on event type specified function * @param {string} type * @param {function} listener */ }, { key: 'on', value: function on(type, listener) { this._addListenner(type, listener, false); } /** * Subscribes on event type specified function to fire only once * @param {string} type * @param {function} listener */ }, { key: 'once', value: function once(type, listener) { this._addListenner(type, listener, true); } /** * Removes event with specified type. If specified listenerFunc - deletes only one listener of specified type * @param {string} eventType * @param {function} [listenerFunc] */ }, { key: 'off', value: function off(eventType, listenerFunc) { var _this = this; var typeIndex = this.events.indexOf(eventType); var hasType = eventType && typeIndex !== -1; if (hasType) { if (!listenerFunc) { delete this._listeners[eventType]; this.events.splice(typeIndex, 1); } else { (function () { var removedEvents = []; var typeListeners = _this._listeners[eventType]; typeListeners.forEach( /** * @param {EventEmitterListenerFunc} fn * @param {number} idx */ function (fn, idx) { if (fn.fn === listenerFunc) { removedEvents.unshift(idx); } }); removedEvents.forEach(function (idx) { typeListeners.splice(idx, 1); }); if (!typeListeners.length) { _this.events.splice(typeIndex, 1); delete _this._listeners[eventType]; } })(); } } } /** * Applies arguments to specified event type * @param {string} eventType * @param {*[]} eventArguments * @protected */ }, { key: '_applyEvents', value: function _applyEvents(eventType, eventArguments) { var typeListeners = this._listeners[eventType]; if (!typeListeners || !typeListeners.length) { if (this._strictMode) { throw 'No listeners specified for event: ' + eventType; } else { return; } } var removableListeners = []; typeListeners.forEach(function (eeListener, idx) { eeListener.fn.apply(null, eventArguments); if (eeListener.once) { removableListeners.unshift(idx); } }); removableListeners.forEach(function (idx) { typeListeners.splice(idx, 1); }); } /** * Emits event with specified type and params. * @param {string} type * @param eventArgs */ }, { key: 'emit', value: function emit(type) { var _this2 = this; for (var _len = arguments.length, eventArgs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { eventArgs[_key - 1] = arguments[_key]; } if (this._emitDelay) { setTimeout(function () { _this2._applyEvents.call(_this2, type, eventArgs); }, this._emitDelay); } else { this._applyEvents(type, eventArgs); } } /** * Emits event with specified type and params synchronously. * @param {string} type * @param eventArgs */ }, { key: 'emitSync', value: function emitSync(type) { for (var _len2 = arguments.length, eventArgs = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { eventArgs[_key2 - 1] = arguments[_key2]; } this._applyEvents(type, eventArgs); } /** * Destroys EventEmitter */ }, { key: 'destroy', value: function destroy() { this._listeners = {}; this.events = []; } }]); return EventEmitter; }(); module.exports = EventEmitter; },{}],4:[function(require,module,exports){ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define([], factory); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(); } else { // Browser globals (root is window) root.FaviconNotification = factory(); } }(this, function() { // Just return a value to define the module export. // This example returns an object, but the module // can return a function as the exported value. // Only run in browser if (typeof document === 'undefined') { console.log('This script only run in browsers.'); return; } // Private properties var _options = {}; var _defaults = { url: '/favicon.ico', color: '#eb361e', lineColor: '#ffffff' }; var _generatedFavicon; var _iconElement; // Provate methods var _addFavicon = function(src) { var head = document.getElementsByTagName('head')[0]; _iconElement = document.createElement('link'); _iconElement.type = 'image/x-icon'; _iconElement.rel = 'icon'; _iconElement.href = src; // remove existing favicons var links = document.getElementsByTagName('link'); for(var i=0, len=links.length; i < len; i++) { var exists = (typeof(links[i]) !== 'undefined'); if (exists && (links[i].getAttribute('rel') || '').match(/\bicon\b/)) { head.removeChild(links[i]); } } head.appendChild(_iconElement); }; var _generateIcon = function(cb) { var img = document.createElement('img'); img.src = _options.url; img.onload = function() { var lineWidth = 2; var canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; var context = canvas.getContext('2d'); context.clearRect(0, 0, img.width, img.height); context.drawImage(img, 0, 0); var centerX = img.width - (img.width / 4.5) - lineWidth; var centerY = img.height - (img.height / 4.5) - lineWidth; var radius = img.width / 4.5; context.fillStyle = _options.color; context.strokeStyle = _options.lineColor; context.lineWidth = lineWidth; context.beginPath(); context.arc(centerX, centerY, radius, 0, Math.PI * 2, false); context.closePath(); context.fill(); context.stroke(); cb(null, context.canvas.toDataURL()); }; }; var _setOptions = function(options) { if (!options) { _options = _defaults; return; } _options = {}; for(var key in _defaults){ _options[key] = options.hasOwnProperty(key) ? options[key] : _defaults[key]; } }; var FaviconNotification = { init: function(options) { _setOptions(options); _generateIcon(function(err, url){ _generatedFavicon = url; }); _addFavicon(_options.url); }, add: function() { if (!_generatedFavicon && !_iconElement) { _setOptions(); _generateIcon(function(err, url) { _generatedFavicon = url; _addFavicon(url); }); } else { _iconElement.href = _generatedFavicon; } }, remove: function() { _iconElement.href = _options.url; } }; return FaviconNotification; })); },{}],5:[function(require,module,exports){ /** * @module fps-indicator */ const raf = require('raf'); const now = require('right-now'); module.exports = fps; function fps (opts) { if (!(this instanceof fps)) return new fps(opts); opts = opts || {}; if (opts.container) { if (typeof opts.container === 'string') { this.container = document.querySelector(opts.container); } else { this.container = opts.container; } } else { this.container = document.body || document.documentElement; } //init fps this.element = document.createElement('div'); this.element.classList.add('fps'); this.element.innerHTML = `
fps 60.0 `; this.container.appendChild(this.element); this.canvas = this.element.querySelector('.fps-canvas'); this.textEl = this.element.querySelector('.fps-text'); this.valueEl = this.element.querySelector('.fps-value'); this.bgEl = this.element.querySelector('.fps-bg'); this.element.style.cssText = ` line-height: 1; position: absolute; z-index: 1; top: 0; right: 0; `; this.canvas.style.cssText = ` position: relative; width: 2em; height: 1em; display: block; float: left; margin-right: .333em; `; this.bgEl.style.cssText = ` position: absolute; height: 1em; width: 2em; background: currentcolor; opacity: .1; `; this.canvas.width = parseInt(getComputedStyle(this.canvas).width) || 1; this.canvas.height = parseInt(getComputedStyle(this.canvas).height) || 1; this.context = this.canvas.getContext('2d'); let ctx = this.context; let w = this.canvas.width; let h = this.canvas.height; let count = 0; let lastTime = 0; let values = opts.values || Array(this.canvas.width); let updatePeriod = opts.updatePeriod || 1000; let maxFps = opts.maxFps || 100; //enable update routine let that = this; raf(function measure () { count++; let t = now(); if (t - lastTime > updatePeriod) { let color = that.color; lastTime = t; values.push(count / (maxFps * updatePeriod * 0.001)); values = values.slice(-w); count = 0; ctx.clearRect(0, 0, w, h); ctx.fillStyle = getComputedStyle(that.canvas).color; for (let i = w; i--;) { let value = values[i]; if (value == null) break; ctx.fillRect(i, h - h * value, 1, h * value); } that.valueEl.innerHTML = (values[values.length - 1]*maxFps).toFixed(1); } raf(measure); }); } },{"raf":13,"right-now":14}],6:[function(require,module,exports){ module.exports = distance /** * Calculates the euclidian distance between two vec2's * * @param {vec2} a the first operand * @param {vec2} b the second operand * @returns {Number} distance between a and b */ function distance(a, b) { var x = b[0] - a[0], y = b[1] - a[1] return Math.sqrt(x*x + y*y) } },{}],7:[function(require,module,exports){ (function (global, factory) { if (typeof define === 'function' && define.amd) { define(['exports', 'module'], factory); } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { factory(exports, module); } else { var mod = { exports: {} }; factory(mod.exports, mod); global.Impetus = mod.exports; } })(this, function (exports, module) { 'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } var stopThresholdDefault = 0.3; var bounceDeceleration = 0.04; var bounceAcceleration = 0.11; var Impetus = function Impetus(_ref) { var _ref$source = _ref.source; var sourceEl = _ref$source === undefined ? document : _ref$source; var updateCallback = _ref.update; var _ref$multiplier = _ref.multiplier; var multiplier = _ref$multiplier === undefined ? 1 : _ref$multiplier; var _ref$friction = _ref.friction; var friction = _ref$friction === undefined ? 0.92 : _ref$friction; var initialValues = _ref.initialValues; var boundX = _ref.boundX; var boundY = _ref.boundY; var _ref$bounce = _ref.bounce; var bounce = _ref$bounce === undefined ? true : _ref$bounce; _classCallCheck(this, Impetus); var boundXmin, boundXmax, boundYmin, boundYmax, pointerLastX, pointerLastY, pointerCurrentX, pointerCurrentY, pointerId, decVelX, decVelY; var targetX = 0; var targetY = 0; var stopThreshold = stopThresholdDefault * multiplier; var ticking = false; var pointerActive = false; var paused = false; var decelerating = false; var trackingPoints = []; /** * Initialize instance */ (function init() { sourceEl = typeof sourceEl === 'string' ? document.querySelector(sourceEl) : sourceEl; if (!sourceEl) { throw new Error('IMPETUS: source not found.'); } if (!updateCallback) { throw new Error('IMPETUS: update function not defined.'); } if (initialValues) { if (initialValues[0]) { targetX = initialValues[0]; } if (initialValues[1]) { targetY = initialValues[1]; } callUpdateCallback(); } // Initialize bound values if (boundX) { boundXmin = boundX[0]; boundXmax = boundX[1]; } if (boundY) { boundYmin = boundY[0]; boundYmax = boundY[1]; } sourceEl.addEventListener('touchstart', onDown); sourceEl.addEventListener('mousedown', onDown); })(); /** * Disable movement processing * @public */ this.pause = function () { pointerActive = false; paused = true; }; /** * Enable movement processing * @public */ this.resume = function () { paused = false; }; /** * Update the current x and y values * @public * @param {Number} x * @param {Number} y */ this.setValues = function (x, y) { if (typeof x === 'number') { targetX = x; } if (typeof y === 'number') { targetY = y; } }; /** * Update the multiplier value * @public * @param {Number} val */ this.setMultiplier = function (val) { multiplier = val; stopThreshold = stopThresholdDefault * multiplier; }; /** * Executes the update function */ function callUpdateCallback() { updateCallback.call(sourceEl, targetX, targetY); } /** * Creates a custom normalized event object from touch and mouse events * @param {Event} ev * @returns {Object} with x, y, and id properties */ function normalizeEvent(ev) { if (ev.type === 'touchmove' || ev.type === 'touchstart' || ev.type === 'touchend') { var touch = ev.targetTouches[0] || ev.changedTouches[0]; return { x: touch.clientX, y: touch.clientY, id: touch.identifier }; } else { // mouse events return { x: ev.clientX, y: ev.clientY, id: null }; } } /** * Initializes movement tracking * @param {Object} ev Normalized event */ function onDown(ev) { var event = normalizeEvent(ev); if (!pointerActive && !paused) { pointerActive = true; decelerating = false; pointerId = event.id; pointerLastX = pointerCurrentX = event.x; pointerLastY = pointerCurrentY = event.y; trackingPoints = []; addTrackingPoint(pointerLastX, pointerLastY); document.addEventListener('touchmove', onMove); document.addEventListener('touchend', onUp); document.addEventListener('touchcancel', stopTracking); document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp); } } /** * Handles move events * @param {Object} ev Normalized event */ function onMove(ev) { ev.preventDefault(); var event = normalizeEvent(ev); if (pointerActive && event.id === pointerId) { pointerCurrentX = event.x; pointerCurrentY = event.y; addTrackingPoint(pointerLastX, pointerLastY); requestTick(); } } /** * Handles up/end events * @param {Object} ev Normalized event */ function onUp(ev) { var event = normalizeEvent(ev); if (pointerActive && event.id === pointerId) { stopTracking(); } } /** * Stops movement tracking, starts animation */ function stopTracking() { pointerActive = false; addTrackingPoint(pointerLastX, pointerLastY); startDecelAnim(); document.removeEventListener('touchmove', onMove); document.removeEventListener('touchend', onUp); document.removeEventListener('touchcancel', stopTracking); document.removeEventListener('mouseup', onUp); document.removeEventListener('mousemove', onMove); } /** * Records movement for the last 100ms * @param {number} x * @param {number} y [description] */ function addTrackingPoint(x, y) { var time = Date.now(); while (trackingPoints.length > 0) { if (time - trackingPoints[0].time <= 100) { break; } trackingPoints.shift(); } trackingPoints.push({ x: x, y: y, time: time }); } /** * Calculate new values, call update function */ function updateAndRender() { var pointerChangeX = pointerCurrentX - pointerLastX; var pointerChangeY = pointerCurrentY - pointerLastY; targetX += pointerChangeX * multiplier; targetY += pointerChangeY * multiplier; if (bounce) { var diff = checkBounds(); if (diff.x !== 0) { targetX -= pointerChangeX * dragOutOfBoundsMultiplier(diff.x) * multiplier; } if (diff.y !== 0) { targetY -= pointerChangeY * dragOutOfBoundsMultiplier(diff.y) * multiplier; } } else { checkBounds(true); } callUpdateCallback(); pointerLastX = pointerCurrentX; pointerLastY = pointerCurrentY; ticking = false; } /** * Returns a value from around 0.5 to 1, based on distance * @param {Number} val */ function dragOutOfBoundsMultiplier(val) { return 0.000005 * Math.pow(val, 2) + 0.0001 * val + 0.55; } /** * prevents animating faster than current framerate */ function requestTick() { if (!ticking) { requestAnimFrame(updateAndRender); } ticking = true; } /** * Determine position relative to bounds * @param {Boolean} restrict Whether to restrict target to bounds */ function checkBounds(restrict) { var xDiff = 0; var yDiff = 0; if (boundXmin !== undefined && targetX < boundXmin) { xDiff = boundXmin - targetX; } else if (boundXmax !== undefined && targetX > boundXmax) { xDiff = boundXmax - targetX; } if (boundYmin !== undefined && targetY < boundYmin) { yDiff = boundYmin - targetY; } else if (boundYmax !== undefined && targetY > boundYmax) { yDiff = boundYmax - targetY; } if (restrict) { if (xDiff !== 0) { targetX = xDiff > 0 ? boundXmin : boundXmax; } if (yDiff !== 0) { targetY = yDiff > 0 ? boundYmin : boundYmax; } } return { x: xDiff, y: yDiff, inBounds: xDiff === 0 && yDiff === 0 }; } /** * Initialize animation of values coming to a stop */ function startDecelAnim() { var firstPoint = trackingPoints[0]; var lastPoint = trackingPoints[trackingPoints.length - 1]; var xOffset = lastPoint.x - firstPoint.x; var yOffset = lastPoint.y - firstPoint.y; var timeOffset = lastPoint.time - firstPoint.time; var D = timeOffset / 15 / multiplier; decVelX = xOffset / D || 0; // prevent NaN decVelY = yOffset / D || 0; var diff = checkBounds(); if (Math.abs(decVelX) > 1 || Math.abs(decVelY) > 1 || !diff.inBounds) { decelerating = true; requestAnimFrame(stepDecelAnim); } } /** * Animates values slowing down */ function stepDecelAnim() { if (!decelerating) { return; } decVelX *= friction; decVelY *= friction; targetX += decVelX; targetY += decVelY; var diff = checkBounds(); if (Math.abs(decVelX) > stopThreshold || Math.abs(decVelY) > stopThreshold || !diff.inBounds) { if (bounce) { var reboundAdjust = 2.5; if (diff.x !== 0) { if (diff.x * decVelX <= 0) { decVelX += diff.x * bounceDeceleration; } else { var adjust = diff.x > 0 ? reboundAdjust : -reboundAdjust; decVelX = (diff.x + adjust) * bounceAcceleration; } } if (diff.y !== 0) { if (diff.y * decVelY <= 0) { decVelY += diff.y * bounceDeceleration; } else { var adjust = diff.y > 0 ? reboundAdjust : -reboundAdjust; decVelY = (diff.y + adjust) * bounceAcceleration; } } } else { if (diff.x !== 0) { if (diff.x > 0) { targetX = boundXmin; } else { targetX = boundXmax; } decVelX = 0; } if (diff.y !== 0) { if (diff.y > 0) { targetY = boundYmin; } else { targetY = boundYmax; } decVelY = 0; } } callUpdateCallback(); requestAnimFrame(stepDecelAnim); } else { decelerating = false; } } } /** * @see http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ */ ; module.exports = Impetus; var requestAnimFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })(); }); },{}],8:[function(require,module,exports){ var rootPosition = { left: 0, top: 0 } module.exports = mouseEventOffset function mouseEventOffset (ev, target, out) { target = target || ev.currentTarget || ev.srcElement if (!Array.isArray(out)) { out = [ 0, 0 ] } var cx = ev.clientX || 0 var cy = ev.clientY || 0 var rect = getBoundingClientOffset(target) out[0] = cx - rect.left out[1] = cy - rect.top return out } function getBoundingClientOffset (element) { if (element === window || element === document || element === document.body) { return rootPosition } else { return element.getBoundingClientRect() } } },{}],9:[function(require,module,exports){ 'use strict' var toPX = require('to-px') module.exports = mouseWheelListen function mouseWheelListen(element, callback, noScroll) { if(typeof element === 'function') { noScroll = !!callback callback = element element = window } var lineHeight = toPX('ex', element) var listener = function(ev) { if(noScroll) { ev.preventDefault() } var dx = ev.deltaX || 0 var dy = ev.deltaY || 0 var dz = ev.deltaZ || 0 var mode = ev.deltaMode var scale = 1 switch(mode) { case 1: scale = lineHeight break case 2: scale = window.innerHeight break } dx *= scale dy *= scale dz *= scale if(dx || dy || dz) { return callback(dx, dy, dz, ev) } } element.addEventListener('wheel', listener) return listener } },{"to-px":15}],10:[function(require,module,exports){ /** * @module pan-zoom * * Events for pan and zoom */ 'use strict'; const Impetus = require('impetus'); const wheel = require('mouse-wheel'); const touchPinch = require('touch-pinch'); const position = require('touch-position'); module.exports = panzoom; function panzoom (target, cb) { if (!target || !(cb instanceof Function)) return false; //enable panning let pos = position({ element: target }); let impetus; let lastY = 0, lastX = 0; impetus = new Impetus({ source: target, update: (x, y) => { let e = { type: 'mouse', dx: x-lastX, dy: y-lastY, dz: 0, x: pos[0], y: pos[1] }; lastX = x; lastY = y; cb(e); }, multiplier: 1, friction: .75 }); //enable zooming wheel(target, (dx, dy, dz, e) => { e.preventDefault(); cb({ type: 'mouse', dx: 0, dy: 0, dz: dy, x: pos[0], y: pos[1] }); }); //mobile pinch zoom let pinch = touchPinch(target); let mult = 2; let initialCoords; pinch.on('start', (curr) => { impetus && impetus.pause(); let [f1, f2] = pinch.fingers; initialCoords = [f2.position[0]*.5 + f1.position[0]*.5, f2.position[1]*.5 + f1.position[1]*.5]; }); pinch.on('end', () => { initialCoords = null; impetus && impetus.resume(); }); pinch.on('change', (curr, prev) => { if (!pinch.pinching || !initialCoords) return; cb({ type: 'touch', dx: 0, dy: 0, dz: -(curr - prev)*mult, x: initialCoords[0], y: initialCoords[1] }); }); } },{"impetus":7,"mouse-wheel":9,"touch-pinch":16,"touch-position":17}],11:[function(require,module,exports){ module.exports = function parseUnit(str, out) { if (!out) out = [ 0, '' ] str = String(str) var num = parseFloat(str, 10) out[0] = num out[1] = str.match(/[\d.\-\+]*\s*(.*)/)[1] || '' return out } },{}],12:[function(require,module,exports){ (function (process){ // Generated by CoffeeScript 1.7.1 (function() { var getNanoSeconds, hrtime, loadTime; if ((typeof performance !== "undefined" && performance !== null) && performance.now) { module.exports = function() { return performance.now(); }; } else if ((typeof process !== "undefined" && process !== null) && process.hrtime) { module.exports = function() { return (getNanoSeconds() - loadTime) / 1e6; }; hrtime = process.hrtime; getNanoSeconds = function() { var hr; hr = hrtime(); return hr[0] * 1e9 + hr[1]; }; loadTime = getNanoSeconds(); } else if (Date.now) { module.exports = function() { return Date.now() - loadTime; }; loadTime = Date.now(); } else { module.exports = function() { return new Date().getTime() - loadTime; }; loadTime = new Date().getTime(); } }).call(this); }).call(this,require('_process')) },{"_process":19}],13:[function(require,module,exports){ (function (global){ var now = require('performance-now') , root = typeof window === 'undefined' ? global : window , vendors = ['moz', 'webkit'] , suffix = 'AnimationFrame' , raf = root['request' + suffix] , caf = root['cancel' + suffix] || root['cancelRequest' + suffix] for(var i = 0; !raf && i < vendors.length; i++) { raf = root[vendors[i] + 'Request' + suffix] caf = root[vendors[i] + 'Cancel' + suffix] || root[vendors[i] + 'CancelRequest' + suffix] } // Some versions of FF have rAF but not cAF if(!raf || !caf) { var last = 0 , id = 0 , queue = [] , frameDuration = 1000 / 60 raf = function(callback) { if(queue.length === 0) { var _now = now() , next = Math.max(0, frameDuration - (_now - last)) last = next + _now setTimeout(function() { var cp = queue.slice(0) // Clear queue here to prevent // callbacks from appending listeners // to the current frame's queue queue.length = 0 for(var i = 0; i < cp.length; i++) { if(!cp[i].cancelled) { try{ cp[i].callback(last) } catch(e) { setTimeout(function() { throw e }, 0) } } } }, Math.round(next)) } queue.push({ handle: ++id, callback: callback, cancelled: false }) return id } caf = function(handle) { for(var i = 0; i < queue.length; i++) { if(queue[i].handle === handle) { queue[i].cancelled = true } } } } module.exports = function(fn) { // Wrap in a new function to prevent // `cancel` potentially being assigned // to the native rAF function return raf.call(root, fn) } module.exports.cancel = function() { caf.apply(root, arguments) } module.exports.polyfill = function() { root.requestAnimationFrame = raf root.cancelAnimationFrame = caf } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"performance-now":12}],14:[function(require,module,exports){ (function (global){ module.exports = global.performance && global.performance.now ? function now() { return performance.now() } : Date.now || function now() { return +new Date } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],15:[function(require,module,exports){ 'use strict' var parseUnit = require('parse-unit') module.exports = toPX var PIXELS_PER_INCH = 96 function getPropertyInPX(element, prop) { var parts = parseUnit(getComputedStyle(element).getPropertyValue(prop)) return parts[0] * toPX(parts[1], element) } //This brutal hack is needed function getSizeBrutal(unit, element) { var testDIV = document.createElement('div') testDIV.style['font-size'] = '128' + unit element.appendChild(testDIV) var size = getPropertyInPX(testDIV, 'font-size') / 128 element.removeChild(testDIV) return size } function toPX(str, element) { element = element || document.body str = (str || 'px').trim().toLowerCase() if(element === window || element === document) { element = document.body } switch(str) { case '%': //Ambiguous, not sure if we should use width or height return element.clientHeight / 100.0 case 'ch': case 'ex': return getSizeBrutal(str, element) case 'em': return getPropertyInPX(element, 'font-size') case 'rem': return getPropertyInPX(document.body, 'font-size') case 'vw': return window.innerWidth/100 case 'vh': return window.innerHeight/100 case 'vmin': return Math.min(window.innerWidth, window.innerHeight) / 100 case 'vmax': return Math.max(window.innerWidth, window.innerHeight) / 100 case 'in': return PIXELS_PER_INCH case 'cm': return PIXELS_PER_INCH / 2.54 case 'mm': return PIXELS_PER_INCH / 25.4 case 'pt': return PIXELS_PER_INCH / 72 case 'pc': return PIXELS_PER_INCH / 6 } return 1 } },{"parse-unit":11}],16:[function(require,module,exports){ var getDistance = require('gl-vec2/distance') var EventEmitter = require('events').EventEmitter var dprop = require('dprop') var eventOffset = require('mouse-event-offset') module.exports = touchPinch function touchPinch (target) { target = target || window var emitter = new EventEmitter() var fingers = [ null, null ] var activeCount = 0 var lastDistance = 0 var ended = false var enabled = false // some read-only values Object.defineProperties(emitter, { pinching: dprop(function () { return activeCount === 2 }), fingers: dprop(function () { return fingers }) }) enable() emitter.enable = enable emitter.disable = disable emitter.indexOfTouch = indexOfTouch return emitter function indexOfTouch (touch) { var id = touch.identifier for (var i = 0; i < fingers.length; i++) { if (fingers[i] && fingers[i].touch && fingers[i].touch.identifier === id) { return i } } return -1 } function enable () { if (enabled) return enabled = true target.addEventListener('touchstart', onTouchStart, false) target.addEventListener('touchmove', onTouchMove, false) target.addEventListener('touchend', onTouchRemoved, false) target.addEventListener('touchcancel', onTouchRemoved, false) } function disable () { if (!enabled) return enabled = false target.removeEventListener('touchstart', onTouchStart, false) target.removeEventListener('touchmove', onTouchMove, false) target.removeEventListener('touchend', onTouchRemoved, false) target.removeEventListener('touchcancel', onTouchRemoved, false) } function onTouchStart (ev) { for (var i = 0; i < ev.changedTouches.length; i++) { var newTouch = ev.changedTouches[i] var id = newTouch.identifier var idx = indexOfTouch(id) if (idx === -1 && activeCount < 2) { var first = activeCount === 0 // newest and previous finger (previous may be undefined) var newIndex = fingers[0] ? 1 : 0 var oldIndex = fingers[0] ? 0 : 1 var newFinger = new Finger() // add to stack fingers[newIndex] = newFinger activeCount++ // update touch event & position newFinger.touch = newTouch eventOffset(newTouch, target, newFinger.position) var oldTouch = fingers[oldIndex] ? fingers[oldIndex].touch : undefined emitter.emit('place', newTouch, oldTouch) if (!first) { var initialDistance = computeDistance() ended = false emitter.emit('start', initialDistance) lastDistance = initialDistance } } } } function onTouchMove (ev) { var changed = false for (var i = 0; i < ev.changedTouches.length; i++) { var movedTouch = ev.changedTouches[i] var idx = indexOfTouch(movedTouch) if (idx !== -1) { changed = true fingers[idx].touch = movedTouch // avoid caching touches eventOffset(movedTouch, target, fingers[idx].position) } } if (activeCount === 2 && changed) { var currentDistance = computeDistance() emitter.emit('change', currentDistance, lastDistance) lastDistance = currentDistance } } function onTouchRemoved (ev) { for (var i = 0; i < ev.changedTouches.length; i++) { var removed = ev.changedTouches[i] var idx = indexOfTouch(removed) if (idx !== -1) { fingers[idx] = null activeCount-- var otherIdx = idx === 0 ? 1 : 0 var otherTouch = fingers[otherIdx] ? fingers[otherIdx].touch : undefined emitter.emit('lift', removed, otherTouch) } } if (!ended && activeCount !== 2) { ended = true emitter.emit('end') } } function computeDistance () { if (activeCount < 2) return 0 return getDistance(fingers[0].position, fingers[1].position) } } function Finger () { this.position = [0, 0] this.touch = null } },{"dprop":2,"events":18,"gl-vec2/distance":6,"mouse-event-offset":8}],17:[function(require,module,exports){ var offset = require('mouse-event-offset'); var EventEmitter = require('events').EventEmitter; function attach (opt) { opt = opt || {}; var element = opt.element || window; var emitter = new EventEmitter(); var position = opt.position || [0, 0]; if (opt.touchstart !== false) { element.addEventListener('mousedown', update, false); element.addEventListener('touchstart', updateTouch, false); } element.addEventListener('mousemove', update, false); element.addEventListener('touchmove', updateTouch, false); emitter.position = position; emitter.dispose = dispose; return emitter; function updateTouch (ev) { var touch = ev.targetTouches[0]; update(touch); } function update (ev) { offset(ev, element, position); emitter.emit('move', ev); } function dispose () { element.removeEventListener('mousemove', update, false); element.removeEventListener('mousedown', update, false); element.removeEventListener('touchmove', updateTouch, false); element.removeEventListener('touchstart', updateTouch, false); } } module.exports = function (opt) { return attach(opt).position; }; module.exports.emitter = function (opt) { return attach(opt); }; },{"events":18,"mouse-event-offset":8}],18:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. function EventEmitter() { this._events = this._events || {}; this._maxListeners = this._maxListeners || undefined; } module.exports = EventEmitter; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. EventEmitter.defaultMaxListeners = 10; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function(n) { if (!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this; }; EventEmitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events.error || (isObject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); err.context = er; throw err; } } } handler = this._events[type]; if (isUndefined(handler)) return false; if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: args = Array.prototype.slice.call(arguments, 1); handler.apply(this, args); } } else if (isObject(handler)) { args = Array.prototype.slice.call(arguments, 1); listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; EventEmitter.prototype.addListener = function(type, listener) { var m; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events) this._events = {}; // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (this._events.newListener) this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener); if (!this._events[type]) // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; else if (isObject(this._events[type])) // If we've already got an array, just append. this._events[type].push(listener); else // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; // Check for listener leak if (isObject(this._events[type]) && !this._events[type].warned) { if (!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter.defaultMaxListeners; } if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); if (typeof console.trace === 'function') { // not supported in IE 10 console.trace(); } } } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { if (!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; // emits a 'removeListener' event iff the listener was removed EventEmitter.prototype.removeListener = function(type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removeListener) this.emit('removeListener', type, listener); } else if (isObject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removeListener) this.emit('removeListener', type, listener); } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removeListener, no need to emit if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners); } else if (listeners) { // LIFO order while (listeners.length) this.removeListener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this; }; EventEmitter.prototype.listeners = function(type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret; }; EventEmitter.prototype.listenerCount = function(type) { if (this._events) { var evlistener = this._events[type]; if (isFunction(evlistener)) return 1; else if (evlistener) return evlistener.length; } return 0; }; EventEmitter.listenerCount = function(emitter, type) { return emitter.listenerCount(type); }; function isFunction(arg) { return typeof arg === 'function'; } function isNumber(arg) { return typeof arg === 'number'; } function isObject(arg) { return typeof arg === 'object' && arg !== null; } function isUndefined(arg) { return arg === void 0; } },{}],19:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it // don't break things. But we need to wrap it in a try catch in case it is // wrapped in strict mode code which doesn't define any globals. It's inside a // function because try/catches deoptimize in certain engines. var cachedSetTimeout; var cachedClearTimeout; function defaultSetTimout() { throw new Error('setTimeout has not been defined'); } function defaultClearTimeout () { throw new Error('clearTimeout has not been defined'); } (function () { try { if (typeof setTimeout === 'function') { cachedSetTimeout = setTimeout; } else { cachedSetTimeout = defaultSetTimout; } } catch (e) { cachedSetTimeout = defaultSetTimout; } try { if (typeof clearTimeout === 'function') { cachedClearTimeout = clearTimeout; } else { cachedClearTimeout = defaultClearTimeout; } } catch (e) { cachedClearTimeout = defaultClearTimeout; } } ()) function runTimeout(fun) { if (cachedSetTimeout === setTimeout) { //normal enviroments in sane situations return setTimeout(fun, 0); } // if setTimeout wasn't available but was latter defined if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { cachedSetTimeout = setTimeout; return setTimeout(fun, 0); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedSetTimeout(fun, 0); } catch(e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedSetTimeout.call(null, fun, 0); } catch(e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error return cachedSetTimeout.call(this, fun, 0); } } } function runClearTimeout(marker) { if (cachedClearTimeout === clearTimeout) { //normal enviroments in sane situations return clearTimeout(marker); } // if clearTimeout wasn't available but was latter defined if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { cachedClearTimeout = clearTimeout; return clearTimeout(marker); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedClearTimeout(marker); } catch (e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedClearTimeout.call(null, marker); } catch (e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. // Some versions of I.E. have different rules for clearTimeout vs setTimeout return cachedClearTimeout.call(this, marker); } } } var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { if (!draining || !currentQueue) { return; } draining = false; if (currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if (queue.length) { drainQueue(); } } function drainQueue() { if (draining) { return; } var timeout = runTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while (++queueIndex < len) { if (currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; runClearTimeout(timeout); } process.nextTick = function (fun) { var args = new Array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { runTimeout(drainQueue); } }; // v8 likes predictible objects function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.binding = function (name) { throw new Error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; },{}]},{},[1]);