From 23acabcb58caabee7789190233f55c25ffe9f156 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Mon, 5 Oct 2015 09:33:29 -0400 Subject: [PATCH 01/17] added POC physics web worker implementation. --- dist/vis.js | 8996 +++++++++-------- dist/vis.min.css | 2 +- dist/vis.physics.worker.js | 1484 +++ gulpfile.js | 8 +- lib/network/modules/PhysicsEngine.js | 234 +- lib/network/modules/PhysicsWorker.js | 216 + lib/network/modules/PhysicsWorkerWrapper.js | 4 + .../components/physics/SpringSolver.js | 9 +- lib/network/options.js | 1 + package.json | 3 +- 10 files changed, 6512 insertions(+), 4445 deletions(-) create mode 100644 dist/vis.physics.worker.js create mode 100644 lib/network/modules/PhysicsWorker.js create mode 100644 lib/network/modules/PhysicsWorkerWrapper.js diff --git a/dist/vis.js b/dist/vis.js index 3388fc4b..60a695ba 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -4,8 +4,8 @@ * * A dynamic, browser-based visualization library. * - * @version 4.9.0 - * @date 2015-10-01 + * @version 4.9.1-SNAPSHOT + * @date 2015-10-05 * * @license * Copyright (C) 2011-2015 Almende B.V, http://almende.com @@ -139,10 +139,10 @@ return /******/ (function(modules) { // webpackBootstrap // Network exports.Network = __webpack_require__(59); exports.network = { - Images: __webpack_require__(117), - dotparser: __webpack_require__(115), - gephiParser: __webpack_require__(116), - allOptions: __webpack_require__(111) + Images: __webpack_require__(118), + dotparser: __webpack_require__(116), + gephiParser: __webpack_require__(117), + allOptions: __webpack_require__(112) }; exports.network.convertDot = function (input) { return exports.network.dotparser.DOTToGraph(input); @@ -563,11 +563,11 @@ return /******/ (function(modules) { // webpackBootstrap // object is an ASP date return new Date(Number(match[1])); // parse number } else { - return moment(object).toDate(); // parse string - } + return moment(object).toDate(); // parse string + } } else { - throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); - } + throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); + } case 'Moment': if (exports.isNumber(object)) { @@ -584,11 +584,11 @@ return /******/ (function(modules) { // webpackBootstrap // object is an ASP date return moment(Number(match[1])); // parse number } else { - return moment(object); // parse string - } + return moment(object); // parse string + } } else { - throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); - } + throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); + } case 'ISODate': if (exports.isNumber(object)) { @@ -603,11 +603,11 @@ return /******/ (function(modules) { // webpackBootstrap // object is an ASP date return new Date(Number(match[1])).toISOString(); // parse number } else { - return new Date(object).toISOString(); // parse string - } + return new Date(object).toISOString(); // parse string + } } else { - throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ISODate'); - } + throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ISODate'); + } case 'ASPDate': if (exports.isNumber(object)) { @@ -621,8 +621,8 @@ return /******/ (function(modules) { // webpackBootstrap // object is an ASP date value = new Date(Number(match[1])).valueOf(); // parse number } else { - value = new Date(object).valueOf(); // parse string - } + value = new Date(object).valueOf(); // parse string + } return '/Date(' + value + ')/'; } else { throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ASPDate'); @@ -854,13 +854,13 @@ return /******/ (function(modules) { // webpackBootstrap if (element.addEventListener) { if (useCapture === undefined) useCapture = false; - if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) { - action = 'DOMMouseScroll'; // For Firefox + if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { + action = "DOMMouseScroll"; // For Firefox } element.addEventListener(action, listener, useCapture); } else { - element.attachEvent('on' + action, listener); // IE browsers + element.attachEvent("on" + action, listener); // IE browsers } }; @@ -876,14 +876,14 @@ return /******/ (function(modules) { // webpackBootstrap // non-IE browsers if (useCapture === undefined) useCapture = false; - if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) { - action = 'DOMMouseScroll'; // For Firefox + if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { + action = "DOMMouseScroll"; // For Firefox } element.removeEventListener(action, listener, useCapture); } else { // IE browsers - element.detachEvent('on' + action, listener); + element.detachEvent("on" + action, listener); } }; @@ -896,8 +896,8 @@ return /******/ (function(modules) { // webpackBootstrap if (event.preventDefault) { event.preventDefault(); // non-IE browsers } else { - event.returnValue = false; // IE browsers - } + event.returnValue = false; // IE browsers + } }; /** @@ -1062,17 +1062,17 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} */ exports.overrideOpacity = function (color, opacity) { - if (color.indexOf('rgba') != -1) { + if (color.indexOf("rgba") != -1) { return color; - } else if (color.indexOf('rgb') != -1) { - var rgb = color.substr(color.indexOf('(') + 1).replace(')', '').split(','); - return 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',' + opacity + ')'; + } else if (color.indexOf("rgb") != -1) { + var rgb = color.substr(color.indexOf("(") + 1).replace(")", "").split(","); + return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + opacity + ")"; } else { var rgb = exports.hexToRGB(color); if (rgb == null) { return color; } else { - return 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + opacity + ')'; + return "rgba(" + rgb.r + "," + rgb.g + "," + rgb.b + "," + opacity + ")"; } } }; @@ -1086,7 +1086,7 @@ return /******/ (function(modules) { // webpackBootstrap * @constructor */ exports.RGBToHex = function (red, green, blue) { - return '#' + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1); + return "#" + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1); }; /** @@ -1302,12 +1302,12 @@ return /******/ (function(modules) { // webpackBootstrap }; exports.isValidRGB = function (rgb) { - rgb = rgb.replace(' ', ''); + rgb = rgb.replace(" ", ""); var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb); return isOk; }; exports.isValidRGBA = function (rgba) { - rgba = rgba.replace(' ', ''); + rgba = rgba.replace(" ", ""); var isOk = /rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(.{1,3})\)/i.test(rgba); return isOk; }; @@ -1320,11 +1320,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} */ exports.selectiveBridgeObject = function (fields, referenceObject) { - if (typeof referenceObject == 'object') { + if (typeof referenceObject == "object") { var objectTo = Object.create(referenceObject); for (var i = 0; i < fields.length; i++) { if (referenceObject.hasOwnProperty(fields[i])) { - if (typeof referenceObject[fields[i]] == 'object') { + if (typeof referenceObject[fields[i]] == "object") { objectTo[fields[i]] = exports.bridgeObject(referenceObject[fields[i]]); } } @@ -1343,11 +1343,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} */ exports.bridgeObject = function (referenceObject) { - if (typeof referenceObject == 'object') { + if (typeof referenceObject == "object") { var objectTo = Object.create(referenceObject); for (var i in referenceObject) { if (referenceObject.hasOwnProperty(i)) { - if (typeof referenceObject[i] == 'object') { + if (typeof referenceObject[i] == "object") { objectTo[i] = exports.bridgeObject(referenceObject[i]); } } @@ -4759,16 +4759,16 @@ return /******/ (function(modules) { // webpackBootstrap /* 4 */ /***/ function(module, exports) { - module.exports = function(module) { - if(!module.webpackPolyfill) { - module.deprecate = function() {}; - module.paths = []; - // module.parent = undefined by default - module.children = []; - module.webpackPolyfill = 1; - } - return module; - } + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; + } /***/ }, @@ -5137,21 +5137,21 @@ return /******/ (function(modules) { // webpackBootstrap var point; if (groupTemplate.style == 'circle') { point = exports.getSVGElement('circle', JSONcontainer, svgContainer); - point.setAttributeNS(null, 'cx', x); - point.setAttributeNS(null, 'cy', y); - point.setAttributeNS(null, 'r', 0.5 * groupTemplate.size); + point.setAttributeNS(null, "cx", x); + point.setAttributeNS(null, "cy", y); + point.setAttributeNS(null, "r", 0.5 * groupTemplate.size); } else { point = exports.getSVGElement('rect', JSONcontainer, svgContainer); - point.setAttributeNS(null, 'x', x - 0.5 * groupTemplate.size); - point.setAttributeNS(null, 'y', y - 0.5 * groupTemplate.size); - point.setAttributeNS(null, 'width', groupTemplate.size); - point.setAttributeNS(null, 'height', groupTemplate.size); + point.setAttributeNS(null, "x", x - 0.5 * groupTemplate.size); + point.setAttributeNS(null, "y", y - 0.5 * groupTemplate.size); + point.setAttributeNS(null, "width", groupTemplate.size); + point.setAttributeNS(null, "height", groupTemplate.size); } if (groupTemplate.styles !== undefined) { - point.setAttributeNS(null, 'style', groupTemplate.styles); + point.setAttributeNS(null, "style", groupTemplate.styles); } - point.setAttributeNS(null, 'class', groupTemplate.className + ' vis-point'); + point.setAttributeNS(null, "class", groupTemplate.className + " vis-point"); //handle label if (labelObj) { @@ -5168,10 +5168,10 @@ return /******/ (function(modules) { // webpackBootstrap } if (labelObj.className) { - label.setAttributeNS(null, 'class', labelObj.className + ' vis-label'); + label.setAttributeNS(null, "class", labelObj.className + " vis-label"); } - label.setAttributeNS(null, 'x', x); - label.setAttributeNS(null, 'y', y); + label.setAttributeNS(null, "x", x); + label.setAttributeNS(null, "y", y); } return point; @@ -5191,13 +5191,13 @@ return /******/ (function(modules) { // webpackBootstrap y -= height; } var rect = exports.getSVGElement('rect', JSONcontainer, svgContainer); - rect.setAttributeNS(null, 'x', x - 0.5 * width); - rect.setAttributeNS(null, 'y', y); - rect.setAttributeNS(null, 'width', width); - rect.setAttributeNS(null, 'height', height); - rect.setAttributeNS(null, 'class', className); + rect.setAttributeNS(null, "x", x - 0.5 * width); + rect.setAttributeNS(null, "y", y); + rect.setAttributeNS(null, "width", width); + rect.setAttributeNS(null, "height", height); + rect.setAttributeNS(null, "class", className); if (style) { - rect.setAttributeNS(null, 'style', style); + rect.setAttributeNS(null, "style", style); } } }; @@ -5827,8 +5827,8 @@ return /******/ (function(modules) { // webpackBootstrap // TODO: extend order by an Object {field:String, direction:String} // where direction can be 'asc' or 'desc' else { - throw new TypeError('Order must be a function or a string'); - } + throw new TypeError('Order must be a function or a string'); + } }; /** @@ -6604,7 +6604,9 @@ return /******/ (function(modules) { // webpackBootstrap if (this._ids[id]) { delete this._ids[id]; removed.push(id); - } else {} + } else { + // nothing interesting for me :-( + } } } @@ -6648,8 +6650,6 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = DataView; - // nothing interesting for me :-( - /***/ }, /* 11 */ /***/ function(module, exports, __webpack_require__) { @@ -6893,9 +6893,11 @@ return /******/ (function(modules) { // webpackBootstrap if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; - } else if (backgroundColor === undefined) {} else { - throw 'Unsupported type of backgroundColor'; - } + } else if (backgroundColor === undefined) { + // use use defaults + } else { + throw 'Unsupported type of backgroundColor'; + } this.frame.style.backgroundColor = fill; this.frame.style.borderColor = stroke; @@ -7601,9 +7603,9 @@ return /******/ (function(modules) { // webpackBootstrap widthMin = dotSize / 2; // px widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function } else { - widthMin = 20; // px - widthMax = 20; // px - } + widthMin = 20; // px + widthMax = 20; // px + } var height = Math.max(this.frame.clientHeight * 0.25, 100); var top = this.margin; @@ -8139,9 +8141,9 @@ return /******/ (function(modules) { // webpackBootstrap strokeStyle = this.axisColor; // TODO: should be customizable } } else { - fillStyle = 'gray'; - strokeStyle = this.axisColor; - } + fillStyle = 'gray'; + strokeStyle = this.axisColor; + } ctx.lineWidth = this._getStrokeWidth(point); ctx.fillStyle = fillStyle; @@ -8157,39 +8159,39 @@ return /******/ (function(modules) { // webpackBootstrap } } } else { - // grid style - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; + // grid style + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; - if (point !== undefined && right !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + if (point !== undefined && right !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - ctx.lineWidth = this._getStrokeWidth(point) * 2; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.stroke(); - } + ctx.lineWidth = this._getStrokeWidth(point) * 2; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.stroke(); + } - if (point !== undefined && top !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + top.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + if (point !== undefined && top !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + top.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - ctx.lineWidth = this._getStrokeWidth(point) * 2; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.stroke(); + ctx.lineWidth = this._getStrokeWidth(point) * 2; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.stroke(); + } } } - } }; Graph3d.prototype._getStrokeWidth = function (point) { @@ -8896,8 +8898,6 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Graph3d; - // use use defaults - /***/ }, /* 12 */ /***/ function(module, exports) { @@ -10416,7 +10416,10 @@ return /******/ (function(modules) { // webpackBootstrap var getEnd = function getEnd(item) { var end = item.data.end != undefined ? item.data.end : item.data.start; return util.convert(end, 'Date').valueOf(); - }; + } + + // calculate the date of the left side and right side of the items given + ; interval = max - min; // ms @@ -10424,8 +10427,6 @@ return /******/ (function(modules) { // webpackBootstrap interval = 10; } factor = interval / _this.props.center.width; - - // calculate the date of the left side and right side of the items given util.forEach(_this.itemSet.items, (function (item) { item.show(); @@ -10794,2891 +10795,2891 @@ return /******/ (function(modules) { // webpackBootstrap /* 22 */ /***/ function(module, exports, __webpack_require__) { - var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.4 - 2014-09-28 - * http://hammerjs.github.io/ - * - * Copyright (c) 2014 Jorik Tangelder; - * Licensed under the MIT license */ - (function(window, document, exportName, undefined) { - 'use strict'; - - var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o']; - var TEST_ELEMENT = document.createElement('div'); + var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.4 - 2014-09-28 + * http://hammerjs.github.io/ + * + * Copyright (c) 2014 Jorik Tangelder; + * Licensed under the MIT license */ + (function(window, document, exportName, undefined) { + 'use strict'; + + var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o']; + var TEST_ELEMENT = document.createElement('div'); + + var TYPE_FUNCTION = 'function'; + + var round = Math.round; + var abs = Math.abs; + var now = Date.now; + + /** + * set a timeout with a given scope + * @param {Function} fn + * @param {Number} timeout + * @param {Object} context + * @returns {number} + */ + function setTimeoutContext(fn, timeout, context) { + return setTimeout(bindFn(fn, context), timeout); + } + + /** + * if the argument is an array, we want to execute the fn on each entry + * if it aint an array we don't want to do a thing. + * this is used by all the methods that accept a single and array argument. + * @param {*|Array} arg + * @param {String} fn + * @param {Object} [context] + * @returns {Boolean} + */ + function invokeArrayArg(arg, fn, context) { + if (Array.isArray(arg)) { + each(arg, context[fn], context); + return true; + } + return false; + } + + /** + * walk objects and arrays + * @param {Object} obj + * @param {Function} iterator + * @param {Object} context + */ + function each(obj, iterator, context) { + var i; + + if (!obj) { + return; + } + + if (obj.forEach) { + obj.forEach(iterator, context); + } else if (obj.length !== undefined) { + i = 0; + while (i < obj.length) { + iterator.call(context, obj[i], i, obj); + i++; + } + } else { + for (i in obj) { + obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); + } + } + } + + /** + * extend object. + * means that properties in dest will be overwritten by the ones in src. + * @param {Object} dest + * @param {Object} src + * @param {Boolean} [merge] + * @returns {Object} dest + */ + function extend(dest, src, merge) { + var keys = Object.keys(src); + var i = 0; + while (i < keys.length) { + if (!merge || (merge && dest[keys[i]] === undefined)) { + dest[keys[i]] = src[keys[i]]; + } + i++; + } + return dest; + } + + /** + * merge the values from src in the dest. + * means that properties that exist in dest will not be overwritten by src + * @param {Object} dest + * @param {Object} src + * @returns {Object} dest + */ + function merge(dest, src) { + return extend(dest, src, true); + } + + /** + * simple class inheritance + * @param {Function} child + * @param {Function} base + * @param {Object} [properties] + */ + function inherit(child, base, properties) { + var baseP = base.prototype, + childP; + + childP = child.prototype = Object.create(baseP); + childP.constructor = child; + childP._super = baseP; + + if (properties) { + extend(childP, properties); + } + } + + /** + * simple function bind + * @param {Function} fn + * @param {Object} context + * @returns {Function} + */ + function bindFn(fn, context) { + return function boundFn() { + return fn.apply(context, arguments); + }; + } + + /** + * let a boolean value also be a function that must return a boolean + * this first item in args will be used as the context + * @param {Boolean|Function} val + * @param {Array} [args] + * @returns {Boolean} + */ + function boolOrFn(val, args) { + if (typeof val == TYPE_FUNCTION) { + return val.apply(args ? args[0] || undefined : undefined, args); + } + return val; + } + + /** + * use the val2 when val1 is undefined + * @param {*} val1 + * @param {*} val2 + * @returns {*} + */ + function ifUndefined(val1, val2) { + return (val1 === undefined) ? val2 : val1; + } + + /** + * addEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler + */ + function addEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.addEventListener(type, handler, false); + }); + } + + /** + * removeEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler + */ + function removeEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.removeEventListener(type, handler, false); + }); + } + + /** + * find if a node is in the given parent + * @method hasParent + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @return {Boolean} found + */ + function hasParent(node, parent) { + while (node) { + if (node == parent) { + return true; + } + node = node.parentNode; + } + return false; + } + + /** + * small indexOf wrapper + * @param {String} str + * @param {String} find + * @returns {Boolean} found + */ + function inStr(str, find) { + return str.indexOf(find) > -1; + } + + /** + * split string on whitespace + * @param {String} str + * @returns {Array} words + */ + function splitStr(str) { + return str.trim().split(/\s+/g); + } + + /** + * find if a array contains the object using indexOf or a simple polyFill + * @param {Array} src + * @param {String} find + * @param {String} [findByKey] + * @return {Boolean|Number} false when not found, or the index + */ + function inArray(src, find, findByKey) { + if (src.indexOf && !findByKey) { + return src.indexOf(find); + } else { + var i = 0; + while (i < src.length) { + if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { + return i; + } + i++; + } + return -1; + } + } + + /** + * convert array-like objects to real arrays + * @param {Object} obj + * @returns {Array} + */ + function toArray(obj) { + return Array.prototype.slice.call(obj, 0); + } + + /** + * unique array with objects based on a key (like 'id') or just by the array's value + * @param {Array} src [{id:1},{id:2},{id:1}] + * @param {String} [key] + * @param {Boolean} [sort=False] + * @returns {Array} [{id:1},{id:2}] + */ + function uniqueArray(src, key, sort) { + var results = []; + var values = []; + var i = 0; + + while (i < src.length) { + var val = key ? src[i][key] : src[i]; + if (inArray(values, val) < 0) { + results.push(src[i]); + } + values[i] = val; + i++; + } + + if (sort) { + if (!key) { + results = results.sort(); + } else { + results = results.sort(function sortUniqueArray(a, b) { + return a[key] > b[key]; + }); + } + } + + return results; + } + + /** + * get the prefixed property + * @param {Object} obj + * @param {String} property + * @returns {String|Undefined} prefixed + */ + function prefixed(obj, property) { + var prefix, prop; + var camelProp = property[0].toUpperCase() + property.slice(1); + + var i = 0; + while (i < VENDOR_PREFIXES.length) { + prefix = VENDOR_PREFIXES[i]; + prop = (prefix) ? prefix + camelProp : property; + + if (prop in obj) { + return prop; + } + i++; + } + return undefined; + } + + /** + * get a unique id + * @returns {number} uniqueId + */ + var _uniqueId = 1; + function uniqueId() { + return _uniqueId++; + } + + /** + * get the window object of an element + * @param {HTMLElement} element + * @returns {DocumentView|Window} + */ + function getWindowForElement(element) { + var doc = element.ownerDocument; + return (doc.defaultView || doc.parentWindow); + } + + var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; + + var SUPPORT_TOUCH = ('ontouchstart' in window); + var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; + var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); + + var INPUT_TYPE_TOUCH = 'touch'; + var INPUT_TYPE_PEN = 'pen'; + var INPUT_TYPE_MOUSE = 'mouse'; + var INPUT_TYPE_KINECT = 'kinect'; + + var COMPUTE_INTERVAL = 25; + + var INPUT_START = 1; + var INPUT_MOVE = 2; + var INPUT_END = 4; + var INPUT_CANCEL = 8; + + var DIRECTION_NONE = 1; + var DIRECTION_LEFT = 2; + var DIRECTION_RIGHT = 4; + var DIRECTION_UP = 8; + var DIRECTION_DOWN = 16; + + var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; + var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; + var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; + + var PROPS_XY = ['x', 'y']; + var PROPS_CLIENT_XY = ['clientX', 'clientY']; + + /** + * create new input type manager + * @param {Manager} manager + * @param {Function} callback + * @returns {Input} + * @constructor + */ + function Input(manager, callback) { + var self = this; + this.manager = manager; + this.callback = callback; + this.element = manager.element; + this.target = manager.options.inputTarget; + + // smaller wrapper around the handler, for the scope and the enabled state of the manager, + // so when disabled the input events are completely bypassed. + this.domHandler = function(ev) { + if (boolOrFn(manager.options.enable, [manager])) { + self.handler(ev); + } + }; + + this.init(); + + } + + Input.prototype = { + /** + * should handle the inputEvent data and trigger the callback + * @virtual + */ + handler: function() { }, + + /** + * bind the events + */ + init: function() { + this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + }, + + /** + * unbind the events + */ + destroy: function() { + this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + } + }; + + /** + * create new input type manager + * called by the Manager constructor + * @param {Hammer} manager + * @returns {Input} + */ + function createInputInstance(manager) { + var Type; + var inputClass = manager.options.inputClass; + + if (inputClass) { + Type = inputClass; + } else if (SUPPORT_POINTER_EVENTS) { + Type = PointerEventInput; + } else if (SUPPORT_ONLY_TOUCH) { + Type = TouchInput; + } else if (!SUPPORT_TOUCH) { + Type = MouseInput; + } else { + Type = TouchMouseInput; + } + return new (Type)(manager, inputHandler); + } + + /** + * handle input events + * @param {Manager} manager + * @param {String} eventType + * @param {Object} input + */ + function inputHandler(manager, eventType, input) { + var pointersLen = input.pointers.length; + var changedPointersLen = input.changedPointers.length; + var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); + var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); + + input.isFirst = !!isFirst; + input.isFinal = !!isFinal; + + if (isFirst) { + manager.session = {}; + } + + // source event is the normalized value of the domEvents + // like 'touchstart, mouseup, pointerdown' + input.eventType = eventType; + + // compute scale, rotation etc + computeInputData(manager, input); + + // emit secret event + manager.emit('hammer.input', input); + + manager.recognize(input); + manager.session.prevInput = input; + } + + /** + * extend the data with some usable properties like scale, rotate, velocity etc + * @param {Object} manager + * @param {Object} input + */ + function computeInputData(manager, input) { + var session = manager.session; + var pointers = input.pointers; + var pointersLength = pointers.length; + + // store the first input to calculate the distance and direction + if (!session.firstInput) { + session.firstInput = simpleCloneInputData(input); + } + + // to compute scale and rotation we need to store the multiple touches + if (pointersLength > 1 && !session.firstMultiple) { + session.firstMultiple = simpleCloneInputData(input); + } else if (pointersLength === 1) { + session.firstMultiple = false; + } + + var firstInput = session.firstInput; + var firstMultiple = session.firstMultiple; + var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; + + var center = input.center = getCenter(pointers); + input.timeStamp = now(); + input.deltaTime = input.timeStamp - firstInput.timeStamp; + + input.angle = getAngle(offsetCenter, center); + input.distance = getDistance(offsetCenter, center); + + computeDeltaXY(session, input); + input.offsetDirection = getDirection(input.deltaX, input.deltaY); + + input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; + input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; + + computeIntervalInputData(session, input); + + // find the correct target + var target = manager.element; + if (hasParent(input.srcEvent.target, target)) { + target = input.srcEvent.target; + } + input.target = target; + } + + function computeDeltaXY(session, input) { + var center = input.center; + var offset = session.offsetDelta || {}; + var prevDelta = session.prevDelta || {}; + var prevInput = session.prevInput || {}; + + if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { + prevDelta = session.prevDelta = { + x: prevInput.deltaX || 0, + y: prevInput.deltaY || 0 + }; + + offset = session.offsetDelta = { + x: center.x, + y: center.y + }; + } + + input.deltaX = prevDelta.x + (center.x - offset.x); + input.deltaY = prevDelta.y + (center.y - offset.y); + } + + /** + * velocity is calculated every x ms + * @param {Object} session + * @param {Object} input + */ + function computeIntervalInputData(session, input) { + var last = session.lastInterval || input, + deltaTime = input.timeStamp - last.timeStamp, + velocity, velocityX, velocityY, direction; + + if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { + var deltaX = last.deltaX - input.deltaX; + var deltaY = last.deltaY - input.deltaY; + + var v = getVelocity(deltaTime, deltaX, deltaY); + velocityX = v.x; + velocityY = v.y; + velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; + direction = getDirection(deltaX, deltaY); + + session.lastInterval = input; + } else { + // use latest velocity info if it doesn't overtake a minimum period + velocity = last.velocity; + velocityX = last.velocityX; + velocityY = last.velocityY; + direction = last.direction; + } + + input.velocity = velocity; + input.velocityX = velocityX; + input.velocityY = velocityY; + input.direction = direction; + } + + /** + * create a simple clone from the input used for storage of firstInput and firstMultiple + * @param {Object} input + * @returns {Object} clonedInputData + */ + function simpleCloneInputData(input) { + // make a simple copy of the pointers because we will get a reference if we don't + // we only need clientXY for the calculations + var pointers = []; + var i = 0; + while (i < input.pointers.length) { + pointers[i] = { + clientX: round(input.pointers[i].clientX), + clientY: round(input.pointers[i].clientY) + }; + i++; + } + + return { + timeStamp: now(), + pointers: pointers, + center: getCenter(pointers), + deltaX: input.deltaX, + deltaY: input.deltaY + }; + } + + /** + * get the center of all the pointers + * @param {Array} pointers + * @return {Object} center contains `x` and `y` properties + */ + function getCenter(pointers) { + var pointersLength = pointers.length; + + // no need to loop when only one touch + if (pointersLength === 1) { + return { + x: round(pointers[0].clientX), + y: round(pointers[0].clientY) + }; + } + + var x = 0, y = 0, i = 0; + while (i < pointersLength) { + x += pointers[i].clientX; + y += pointers[i].clientY; + i++; + } + + return { + x: round(x / pointersLength), + y: round(y / pointersLength) + }; + } + + /** + * calculate the velocity between two points. unit is in px per ms. + * @param {Number} deltaTime + * @param {Number} x + * @param {Number} y + * @return {Object} velocity `x` and `y` + */ + function getVelocity(deltaTime, x, y) { + return { + x: x / deltaTime || 0, + y: y / deltaTime || 0 + }; + } + + /** + * get the direction between two points + * @param {Number} x + * @param {Number} y + * @return {Number} direction + */ + function getDirection(x, y) { + if (x === y) { + return DIRECTION_NONE; + } + + if (abs(x) >= abs(y)) { + return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return y > 0 ? DIRECTION_UP : DIRECTION_DOWN; + } + + /** + * calculate the absolute distance between two points + * @param {Object} p1 {x, y} + * @param {Object} p2 {x, y} + * @param {Array} [props] containing x and y keys + * @return {Number} distance + */ + function getDistance(p1, p2, props) { + if (!props) { + props = PROPS_XY; + } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; + + return Math.sqrt((x * x) + (y * y)); + } + + /** + * calculate the angle between two coordinates + * @param {Object} p1 + * @param {Object} p2 + * @param {Array} [props] containing x and y keys + * @return {Number} angle + */ + function getAngle(p1, p2, props) { + if (!props) { + props = PROPS_XY; + } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; + return Math.atan2(y, x) * 180 / Math.PI; + } + + /** + * calculate the rotation degrees between two pointersets + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} rotation + */ + function getRotation(start, end) { + return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY); + } + + /** + * calculate the scale factor between two pointersets + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} scale + */ + function getScale(start, end) { + return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); + } + + var MOUSE_INPUT_MAP = { + mousedown: INPUT_START, + mousemove: INPUT_MOVE, + mouseup: INPUT_END + }; + + var MOUSE_ELEMENT_EVENTS = 'mousedown'; + var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; + + /** + * Mouse events input + * @constructor + * @extends Input + */ + function MouseInput() { + this.evEl = MOUSE_ELEMENT_EVENTS; + this.evWin = MOUSE_WINDOW_EVENTS; + + this.allow = true; // used by Input.TouchMouse to disable mouse events + this.pressed = false; // mousedown state + + Input.apply(this, arguments); + } + + inherit(MouseInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function MEhandler(ev) { + var eventType = MOUSE_INPUT_MAP[ev.type]; + + // on start we want to have the left mouse button down + if (eventType & INPUT_START && ev.button === 0) { + this.pressed = true; + } + + if (eventType & INPUT_MOVE && ev.which !== 1) { + eventType = INPUT_END; + } + + // mouse must be down, and mouse events are allowed (see the TouchMouse input) + if (!this.pressed || !this.allow) { + return; + } + + if (eventType & INPUT_END) { + this.pressed = false; + } + + this.callback(this.manager, eventType, { + pointers: [ev], + changedPointers: [ev], + pointerType: INPUT_TYPE_MOUSE, + srcEvent: ev + }); + } + }); + + var POINTER_INPUT_MAP = { + pointerdown: INPUT_START, + pointermove: INPUT_MOVE, + pointerup: INPUT_END, + pointercancel: INPUT_CANCEL, + pointerout: INPUT_CANCEL + }; + + // in IE10 the pointer types is defined as an enum + var IE10_POINTER_TYPE_ENUM = { + 2: INPUT_TYPE_TOUCH, + 3: INPUT_TYPE_PEN, + 4: INPUT_TYPE_MOUSE, + 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 + }; + + var POINTER_ELEMENT_EVENTS = 'pointerdown'; + var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; + + // IE10 has prefixed support, and case-sensitive + if (window.MSPointerEvent) { + POINTER_ELEMENT_EVENTS = 'MSPointerDown'; + POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; + } + + /** + * Pointer events input + * @constructor + * @extends Input + */ + function PointerEventInput() { + this.evEl = POINTER_ELEMENT_EVENTS; + this.evWin = POINTER_WINDOW_EVENTS; + + Input.apply(this, arguments); + + this.store = (this.manager.session.pointerEvents = []); + } + + inherit(PointerEventInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function PEhandler(ev) { + var store = this.store; + var removePointer = false; + + var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); + var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; + var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; + + var isTouch = (pointerType == INPUT_TYPE_TOUCH); + + // get index of the event in the store + var storeIndex = inArray(store, ev.pointerId, 'pointerId'); + + // start and mouse must be down + if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { + if (storeIndex < 0) { + store.push(ev); + storeIndex = store.length - 1; + } + } else if (eventType & (INPUT_END | INPUT_CANCEL)) { + removePointer = true; + } + + // it not found, so the pointer hasn't been down (so it's probably a hover) + if (storeIndex < 0) { + return; + } + + // update the event in the store + store[storeIndex] = ev; + + this.callback(this.manager, eventType, { + pointers: store, + changedPointers: [ev], + pointerType: pointerType, + srcEvent: ev + }); + + if (removePointer) { + // remove from the store + store.splice(storeIndex, 1); + } + } + }); + + var SINGLE_TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL + }; + + var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; + var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; + + /** + * Touch events input + * @constructor + * @extends Input + */ + function SingleTouchInput() { + this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; + this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; + this.started = false; + + Input.apply(this, arguments); + } + + inherit(SingleTouchInput, Input, { + handler: function TEhandler(ev) { + var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; + + // should we handle the touch events? + if (type === INPUT_START) { + this.started = true; + } + + if (!this.started) { + return; + } + + var touches = normalizeSingleTouches.call(this, ev, type); + + // when done, reset the started state + if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { + this.started = false; + } + + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); + } + }); + + /** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ + function normalizeSingleTouches(ev, type) { + var all = toArray(ev.touches); + var changed = toArray(ev.changedTouches); + + if (type & (INPUT_END | INPUT_CANCEL)) { + all = uniqueArray(all.concat(changed), 'identifier', true); + } + + return [all, changed]; + } + + var TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL + }; + + var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; + + /** + * Multi-user touch events input + * @constructor + * @extends Input + */ + function TouchInput() { + this.evTarget = TOUCH_TARGET_EVENTS; + this.targetIds = {}; + + Input.apply(this, arguments); + } + + inherit(TouchInput, Input, { + handler: function MTEhandler(ev) { + var type = TOUCH_INPUT_MAP[ev.type]; + var touches = getTouches.call(this, ev, type); + if (!touches) { + return; + } + + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); + } + }); + + /** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ + function getTouches(ev, type) { + var allTouches = toArray(ev.touches); + var targetIds = this.targetIds; + + // when there is only one touch, the process can be simplified + if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { + targetIds[allTouches[0].identifier] = true; + return [allTouches, allTouches]; + } + + var i, + targetTouches, + changedTouches = toArray(ev.changedTouches), + changedTargetTouches = [], + target = this.target; + + // get target touches from touches + targetTouches = allTouches.filter(function(touch) { + return hasParent(touch.target, target); + }); + + // collect touches + if (type === INPUT_START) { + i = 0; + while (i < targetTouches.length) { + targetIds[targetTouches[i].identifier] = true; + i++; + } + } + + // filter changed touches to only contain touches that exist in the collected target ids + i = 0; + while (i < changedTouches.length) { + if (targetIds[changedTouches[i].identifier]) { + changedTargetTouches.push(changedTouches[i]); + } + + // cleanup removed touches + if (type & (INPUT_END | INPUT_CANCEL)) { + delete targetIds[changedTouches[i].identifier]; + } + i++; + } + + if (!changedTargetTouches.length) { + return; + } + + return [ + // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' + uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), + changedTargetTouches + ]; + } + + /** + * Combined touch and mouse input + * + * Touch has a higher priority then mouse, and while touching no mouse events are allowed. + * This because touch devices also emit mouse events while doing a touch. + * + * @constructor + * @extends Input + */ + function TouchMouseInput() { + Input.apply(this, arguments); + + var handler = bindFn(this.handler, this); + this.touch = new TouchInput(this.manager, handler); + this.mouse = new MouseInput(this.manager, handler); + } + + inherit(TouchMouseInput, Input, { + /** + * handle mouse and touch events + * @param {Hammer} manager + * @param {String} inputEvent + * @param {Object} inputData + */ + handler: function TMEhandler(manager, inputEvent, inputData) { + var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), + isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); + + // when we're in a touch event, so block all upcoming mouse events + // most mobile browser also emit mouseevents, right after touchstart + if (isTouch) { + this.mouse.allow = false; + } else if (isMouse && !this.mouse.allow) { + return; + } + + // reset the allowMouse when we're done + if (inputEvent & (INPUT_END | INPUT_CANCEL)) { + this.mouse.allow = true; + } + + this.callback(manager, inputEvent, inputData); + }, + + /** + * remove the event listeners + */ + destroy: function destroy() { + this.touch.destroy(); + this.mouse.destroy(); + } + }); + + var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); + var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; + + // magical touchAction value + var TOUCH_ACTION_COMPUTE = 'compute'; + var TOUCH_ACTION_AUTO = 'auto'; + var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented + var TOUCH_ACTION_NONE = 'none'; + var TOUCH_ACTION_PAN_X = 'pan-x'; + var TOUCH_ACTION_PAN_Y = 'pan-y'; + + /** + * Touch Action + * sets the touchAction property or uses the js alternative + * @param {Manager} manager + * @param {String} value + * @constructor + */ + function TouchAction(manager, value) { + this.manager = manager; + this.set(value); + } + + TouchAction.prototype = { + /** + * set the touchAction value on the element or enable the polyfill + * @param {String} value + */ + set: function(value) { + // find out the touch-action by the event handlers + if (value == TOUCH_ACTION_COMPUTE) { + value = this.compute(); + } + + if (NATIVE_TOUCH_ACTION) { + this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; + } + this.actions = value.toLowerCase().trim(); + }, + + /** + * just re-set the touchAction value + */ + update: function() { + this.set(this.manager.options.touchAction); + }, + + /** + * compute the value for the touchAction property based on the recognizer's settings + * @returns {String} value + */ + compute: function() { + var actions = []; + each(this.manager.recognizers, function(recognizer) { + if (boolOrFn(recognizer.options.enable, [recognizer])) { + actions = actions.concat(recognizer.getTouchAction()); + } + }); + return cleanTouchActions(actions.join(' ')); + }, + + /** + * this method is called on each input cycle and provides the preventing of the browser behavior + * @param {Object} input + */ + preventDefaults: function(input) { + // not needed with native support for the touchAction property + if (NATIVE_TOUCH_ACTION) { + return; + } + + var srcEvent = input.srcEvent; + var direction = input.offsetDirection; + + // if the touch action did prevented once this session + if (this.manager.session.prevented) { + srcEvent.preventDefault(); + return; + } + + var actions = this.actions; + var hasNone = inStr(actions, TOUCH_ACTION_NONE); + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); + + if (hasNone || + (hasPanY && direction & DIRECTION_HORIZONTAL) || + (hasPanX && direction & DIRECTION_VERTICAL)) { + return this.preventSrc(srcEvent); + } + }, + + /** + * call preventDefault to prevent the browser's default behavior (scrolling in most cases) + * @param {Object} srcEvent + */ + preventSrc: function(srcEvent) { + this.manager.session.prevented = true; + srcEvent.preventDefault(); + } + }; + + /** + * when the touchActions are collected they are not a valid value, so we need to clean things up. * + * @param {String} actions + * @returns {*} + */ + function cleanTouchActions(actions) { + // none + if (inStr(actions, TOUCH_ACTION_NONE)) { + return TOUCH_ACTION_NONE; + } + + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); + + // pan-x and pan-y can be combined + if (hasPanX && hasPanY) { + return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y; + } + + // pan-x OR pan-y + if (hasPanX || hasPanY) { + return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; + } + + // manipulation + if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { + return TOUCH_ACTION_MANIPULATION; + } + + return TOUCH_ACTION_AUTO; + } + + /** + * Recognizer flow explained; * + * All recognizers have the initial state of POSSIBLE when a input session starts. + * The definition of a input session is from the first input until the last input, with all it's movement in it. * + * Example session for mouse-input: mousedown -> mousemove -> mouseup + * + * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed + * which determines with state it should be. + * + * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to + * POSSIBLE to give it another change on the next cycle. + * + * Possible + * | + * +-----+---------------+ + * | | + * +-----+-----+ | + * | | | + * Failed Cancelled | + * +-------+------+ + * | | + * Recognized Began + * | + * Changed + * | + * Ended/Recognized + */ + var STATE_POSSIBLE = 1; + var STATE_BEGAN = 2; + var STATE_CHANGED = 4; + var STATE_ENDED = 8; + var STATE_RECOGNIZED = STATE_ENDED; + var STATE_CANCELLED = 16; + var STATE_FAILED = 32; + + /** + * Recognizer + * Every recognizer needs to extend from this class. + * @constructor + * @param {Object} options + */ + function Recognizer(options) { + this.id = uniqueId(); + + this.manager = null; + this.options = merge(options || {}, this.defaults); + + // default is enable true + this.options.enable = ifUndefined(this.options.enable, true); + + this.state = STATE_POSSIBLE; + + this.simultaneous = {}; + this.requireFail = []; + } + + Recognizer.prototype = { + /** + * @virtual + * @type {Object} + */ + defaults: {}, + + /** + * set options + * @param {Object} options + * @return {Recognizer} + */ + set: function(options) { + extend(this.options, options); + + // also update the touchAction, in case something changed about the directions/enabled state + this.manager && this.manager.touchAction.update(); + return this; + }, + + /** + * recognize simultaneous with an other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + recognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { + return this; + } + + var simultaneous = this.simultaneous; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (!simultaneous[otherRecognizer.id]) { + simultaneous[otherRecognizer.id] = otherRecognizer; + otherRecognizer.recognizeWith(this); + } + return this; + }, + + /** + * drop the simultaneous link. it doesnt remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRecognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { + return this; + } + + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + delete this.simultaneous[otherRecognizer.id]; + return this; + }, + + /** + * recognizer can only run when an other is failing + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + requireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { + return this; + } + + var requireFail = this.requireFail; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (inArray(requireFail, otherRecognizer) === -1) { + requireFail.push(otherRecognizer); + otherRecognizer.requireFailure(this); + } + return this; + }, + + /** + * drop the requireFailure link. it does not remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRequireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { + return this; + } + + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + var index = inArray(this.requireFail, otherRecognizer); + if (index > -1) { + this.requireFail.splice(index, 1); + } + return this; + }, + + /** + * has require failures boolean + * @returns {boolean} + */ + hasRequireFailures: function() { + return this.requireFail.length > 0; + }, + + /** + * if the recognizer can recognize simultaneous with an other recognizer + * @param {Recognizer} otherRecognizer + * @returns {Boolean} + */ + canRecognizeWith: function(otherRecognizer) { + return !!this.simultaneous[otherRecognizer.id]; + }, + + /** + * You should use `tryEmit` instead of `emit` directly to check + * that all the needed recognizers has failed before emitting. + * @param {Object} input + */ + emit: function(input) { + var self = this; + var state = this.state; + + function emit(withState) { + self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input); + } + + // 'panstart' and 'panmove' + if (state < STATE_ENDED) { + emit(true); + } + + emit(); // simple 'eventName' events + + // panend and pancancel + if (state >= STATE_ENDED) { + emit(true); + } + }, + + /** + * Check that all the require failure recognizers has failed, + * if true, it emits a gesture event, + * otherwise, setup the state to FAILED. + * @param {Object} input + */ + tryEmit: function(input) { + if (this.canEmit()) { + return this.emit(input); + } + // it's failing anyway + this.state = STATE_FAILED; + }, + + /** + * can we emit? + * @returns {boolean} + */ + canEmit: function() { + var i = 0; + while (i < this.requireFail.length) { + if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { + return false; + } + i++; + } + return true; + }, + + /** + * update the recognizer + * @param {Object} inputData + */ + recognize: function(inputData) { + // make a new copy of the inputData + // so we can change the inputData without messing up the other recognizers + var inputDataClone = extend({}, inputData); + + // is is enabled and allow recognizing? + if (!boolOrFn(this.options.enable, [this, inputDataClone])) { + this.reset(); + this.state = STATE_FAILED; + return; + } + + // reset when we've reached the end + if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { + this.state = STATE_POSSIBLE; + } + + this.state = this.process(inputDataClone); + + // the recognizer has recognized a gesture + // so trigger an event + if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { + this.tryEmit(inputDataClone); + } + }, + + /** + * return the state of the recognizer + * the actual recognizing happens in this method + * @virtual + * @param {Object} inputData + * @returns {Const} STATE + */ + process: function(inputData) { }, // jshint ignore:line + + /** + * return the preferred touch-action + * @virtual + * @returns {Array} + */ + getTouchAction: function() { }, + + /** + * called when the gesture isn't allowed to recognize + * like when another is being recognized or it is disabled + * @virtual + */ + reset: function() { } + }; + + /** + * get a usable string, used as event postfix + * @param {Const} state + * @returns {String} state + */ + function stateStr(state) { + if (state & STATE_CANCELLED) { + return 'cancel'; + } else if (state & STATE_ENDED) { + return 'end'; + } else if (state & STATE_CHANGED) { + return 'move'; + } else if (state & STATE_BEGAN) { + return 'start'; + } + return ''; + } + + /** + * direction cons to string + * @param {Const} direction + * @returns {String} + */ + function directionStr(direction) { + if (direction == DIRECTION_DOWN) { + return 'down'; + } else if (direction == DIRECTION_UP) { + return 'up'; + } else if (direction == DIRECTION_LEFT) { + return 'left'; + } else if (direction == DIRECTION_RIGHT) { + return 'right'; + } + return ''; + } + + /** + * get a recognizer by name if it is bound to a manager + * @param {Recognizer|String} otherRecognizer + * @param {Recognizer} recognizer + * @returns {Recognizer} + */ + function getRecognizerByNameIfManager(otherRecognizer, recognizer) { + var manager = recognizer.manager; + if (manager) { + return manager.get(otherRecognizer); + } + return otherRecognizer; + } + + /** + * This recognizer is just used as a base for the simple attribute recognizers. + * @constructor + * @extends Recognizer + */ + function AttrRecognizer() { + Recognizer.apply(this, arguments); + } + + inherit(AttrRecognizer, Recognizer, { + /** + * @namespace + * @memberof AttrRecognizer + */ + defaults: { + /** + * @type {Number} + * @default 1 + */ + pointers: 1 + }, + + /** + * Used to check if it the recognizer receives valid input, like input.distance > 10. + * @memberof AttrRecognizer + * @param {Object} input + * @returns {Boolean} recognized + */ + attrTest: function(input) { + var optionPointers = this.options.pointers; + return optionPointers === 0 || input.pointers.length === optionPointers; + }, + + /** + * Process the input and return the state for the recognizer + * @memberof AttrRecognizer + * @param {Object} input + * @returns {*} State + */ + process: function(input) { + var state = this.state; + var eventType = input.eventType; + + var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); + var isValid = this.attrTest(input); + + // on cancel input and we've recognized before, return STATE_CANCELLED + if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { + return state | STATE_CANCELLED; + } else if (isRecognized || isValid) { + if (eventType & INPUT_END) { + return state | STATE_ENDED; + } else if (!(state & STATE_BEGAN)) { + return STATE_BEGAN; + } + return state | STATE_CHANGED; + } + return STATE_FAILED; + } + }); + + /** + * Pan + * Recognized when the pointer is down and moved in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ + function PanRecognizer() { + AttrRecognizer.apply(this, arguments); + + this.pX = null; + this.pY = null; + } + + inherit(PanRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PanRecognizer + */ + defaults: { + event: 'pan', + threshold: 10, + pointers: 1, + direction: DIRECTION_ALL + }, + + getTouchAction: function() { + var direction = this.options.direction; + var actions = []; + if (direction & DIRECTION_HORIZONTAL) { + actions.push(TOUCH_ACTION_PAN_Y); + } + if (direction & DIRECTION_VERTICAL) { + actions.push(TOUCH_ACTION_PAN_X); + } + return actions; + }, + + directionTest: function(input) { + var options = this.options; + var hasMoved = true; + var distance = input.distance; + var direction = input.direction; + var x = input.deltaX; + var y = input.deltaY; + + // lock to axis? + if (!(direction & options.direction)) { + if (options.direction & DIRECTION_HORIZONTAL) { + direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; + hasMoved = x != this.pX; + distance = Math.abs(input.deltaX); + } else { + direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; + hasMoved = y != this.pY; + distance = Math.abs(input.deltaY); + } + } + input.direction = direction; + return hasMoved && distance > options.threshold && direction & options.direction; + }, + + attrTest: function(input) { + return AttrRecognizer.prototype.attrTest.call(this, input) && + (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); + }, + + emit: function(input) { + this.pX = input.deltaX; + this.pY = input.deltaY; + + var direction = directionStr(input.direction); + if (direction) { + this.manager.emit(this.options.event + direction, input); + } + + this._super.emit.call(this, input); + } + }); + + /** + * Pinch + * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). + * @constructor + * @extends AttrRecognizer + */ + function PinchRecognizer() { + AttrRecognizer.apply(this, arguments); + } + + inherit(PinchRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'pinch', + threshold: 0, + pointers: 2 + }, + + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, + + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); + }, + + emit: function(input) { + this._super.emit.call(this, input); + if (input.scale !== 1) { + var inOut = input.scale < 1 ? 'in' : 'out'; + this.manager.emit(this.options.event + inOut, input); + } + } + }); + + /** + * Press + * Recognized when the pointer is down for x ms without any movement. + * @constructor + * @extends Recognizer + */ + function PressRecognizer() { + Recognizer.apply(this, arguments); + + this._timer = null; + this._input = null; + } + + inherit(PressRecognizer, Recognizer, { + /** + * @namespace + * @memberof PressRecognizer + */ + defaults: { + event: 'press', + pointers: 1, + time: 500, // minimal time of the pointer to be pressed + threshold: 5 // a minimal movement is ok, but keep it low + }, + + getTouchAction: function() { + return [TOUCH_ACTION_AUTO]; + }, + + process: function(input) { + var options = this.options; + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTime = input.deltaTime > options.time; + + this._input = input; + + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { + this.reset(); + } else if (input.eventType & INPUT_START) { + this.reset(); + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.time, this); + } else if (input.eventType & INPUT_END) { + return STATE_RECOGNIZED; + } + return STATE_FAILED; + }, + + reset: function() { + clearTimeout(this._timer); + }, + + emit: function(input) { + if (this.state !== STATE_RECOGNIZED) { + return; + } + + if (input && (input.eventType & INPUT_END)) { + this.manager.emit(this.options.event + 'up', input); + } else { + this._input.timeStamp = now(); + this.manager.emit(this.options.event, this._input); + } + } + }); + + /** + * Rotate + * Recognized when two or more pointer are moving in a circular motion. + * @constructor + * @extends AttrRecognizer + */ + function RotateRecognizer() { + AttrRecognizer.apply(this, arguments); + } + + inherit(RotateRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof RotateRecognizer + */ + defaults: { + event: 'rotate', + threshold: 0, + pointers: 2 + }, + + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, + + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); + } + }); + + /** + * Swipe + * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ + function SwipeRecognizer() { + AttrRecognizer.apply(this, arguments); + } + + inherit(SwipeRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof SwipeRecognizer + */ + defaults: { + event: 'swipe', + threshold: 10, + velocity: 0.65, + direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, + pointers: 1 + }, + + getTouchAction: function() { + return PanRecognizer.prototype.getTouchAction.call(this); + }, + + attrTest: function(input) { + var direction = this.options.direction; + var velocity; + + if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { + velocity = input.velocity; + } else if (direction & DIRECTION_HORIZONTAL) { + velocity = input.velocityX; + } else if (direction & DIRECTION_VERTICAL) { + velocity = input.velocityY; + } + + return this._super.attrTest.call(this, input) && + direction & input.direction && + input.distance > this.options.threshold && + abs(velocity) > this.options.velocity && input.eventType & INPUT_END; + }, + + emit: function(input) { + var direction = directionStr(input.direction); + if (direction) { + this.manager.emit(this.options.event + direction, input); + } + + this.manager.emit(this.options.event, input); + } + }); + + /** + * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur + * between the given interval and position. The delay option can be used to recognize multi-taps without firing + * a single tap. + * + * The eventData from the emitted event contains the property `tapCount`, which contains the amount of + * multi-taps being recognized. + * @constructor + * @extends Recognizer + */ + function TapRecognizer() { + Recognizer.apply(this, arguments); + + // previous time and center, + // used for tap counting + this.pTime = false; + this.pCenter = false; + + this._timer = null; + this._input = null; + this.count = 0; + } + + inherit(TapRecognizer, Recognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'tap', + pointers: 1, + taps: 1, + interval: 300, // max time between the multi-tap taps + time: 250, // max time of the pointer to be down (like finger on the screen) + threshold: 2, // a minimal movement is ok, but keep it low + posThreshold: 10 // a multi-tap can be a bit off the initial position + }, + + getTouchAction: function() { + return [TOUCH_ACTION_MANIPULATION]; + }, + + process: function(input) { + var options = this.options; + + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTouchTime = input.deltaTime < options.time; + + this.reset(); + + if ((input.eventType & INPUT_START) && (this.count === 0)) { + return this.failTimeout(); + } + + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (validMovement && validTouchTime && validPointers) { + if (input.eventType != INPUT_END) { + return this.failTimeout(); + } + + var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; + var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; + + this.pTime = input.timeStamp; + this.pCenter = input.center; + + if (!validMultiTap || !validInterval) { + this.count = 1; + } else { + this.count += 1; + } + + this._input = input; + + // if tap count matches we have recognized it, + // else it has began recognizing... + var tapCount = this.count % options.taps; + if (tapCount === 0) { + // no failing requirements, immediately trigger the tap event + // or wait as long as the multitap interval to trigger + if (!this.hasRequireFailures()) { + return STATE_RECOGNIZED; + } else { + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.interval, this); + return STATE_BEGAN; + } + } + } + return STATE_FAILED; + }, + + failTimeout: function() { + this._timer = setTimeoutContext(function() { + this.state = STATE_FAILED; + }, this.options.interval, this); + return STATE_FAILED; + }, + + reset: function() { + clearTimeout(this._timer); + }, + + emit: function() { + if (this.state == STATE_RECOGNIZED ) { + this._input.tapCount = this.count; + this.manager.emit(this.options.event, this._input); + } + } + }); + + /** + * Simple way to create an manager with a default set of recognizers. + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ + function Hammer(element, options) { + options = options || {}; + options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); + return new Manager(element, options); + } + + /** + * @const {string} + */ + Hammer.VERSION = '2.0.4'; + + /** + * default settings + * @namespace + */ + Hammer.defaults = { + /** + * set if DOM events are being triggered. + * But this is slower and unused by simple implementations, so disabled by default. + * @type {Boolean} + * @default false + */ + domEvents: false, + + /** + * The value for the touchAction property/fallback. + * When set to `compute` it will magically set the correct value based on the added recognizers. + * @type {String} + * @default compute + */ + touchAction: TOUCH_ACTION_COMPUTE, + + /** + * @type {Boolean} + * @default true + */ + enable: true, + + /** + * EXPERIMENTAL FEATURE -- can be removed/changed + * Change the parent input target element. + * If Null, then it is being set the to main element. + * @type {Null|EventTarget} + * @default null + */ + inputTarget: null, + + /** + * force an input class + * @type {Null|Function} + * @default null + */ + inputClass: null, + + /** + * Default recognizer setup when calling `Hammer()` + * When creating a new Manager these will be skipped. + * @type {Array} + */ + preset: [ + // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] + [RotateRecognizer, { enable: false }], + [PinchRecognizer, { enable: false }, ['rotate']], + [SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }], + [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], + [TapRecognizer], + [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], + [PressRecognizer] + ], + + /** + * Some CSS properties can be used to improve the working of Hammer. + * Add them to this method and they will be set when creating a new Manager. + * @namespace + */ + cssProps: { + /** + * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userSelect: 'none', + + /** + * Disable the Windows Phone grippers when pressing an element. + * @type {String} + * @default 'none' + */ + touchSelect: 'none', + + /** + * Disables the default callout shown when you touch and hold a touch target. + * On iOS, when you touch and hold a touch target such as a link, Safari displays + * a callout containing information about the link. This property allows you to disable that callout. + * @type {String} + * @default 'none' + */ + touchCallout: 'none', + + /** + * Specifies whether zooming is enabled. Used by IE10> + * @type {String} + * @default 'none' + */ + contentZooming: 'none', + + /** + * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userDrag: 'none', + + /** + * Overrides the highlight color shown when the user taps a link or a JavaScript + * clickable element in iOS. This property obeys the alpha value, if specified. + * @type {String} + * @default 'rgba(0,0,0,0)' + */ + tapHighlightColor: 'rgba(0,0,0,0)' + } + }; + + var STOP = 1; + var FORCED_STOP = 2; + + /** + * Manager + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ + function Manager(element, options) { + options = options || {}; + + this.options = merge(options, Hammer.defaults); + this.options.inputTarget = this.options.inputTarget || element; + + this.handlers = {}; + this.session = {}; + this.recognizers = []; + + this.element = element; + this.input = createInputInstance(this); + this.touchAction = new TouchAction(this, this.options.touchAction); + + toggleCssProps(this, true); + + each(options.recognizers, function(item) { + var recognizer = this.add(new (item[0])(item[1])); + item[2] && recognizer.recognizeWith(item[2]); + item[3] && recognizer.requireFailure(item[3]); + }, this); + } + + Manager.prototype = { + /** + * set options + * @param {Object} options + * @returns {Manager} + */ + set: function(options) { + extend(this.options, options); + + // Options that need a little more setup + if (options.touchAction) { + this.touchAction.update(); + } + if (options.inputTarget) { + // Clean up existing event listeners and reinitialize + this.input.destroy(); + this.input.target = options.inputTarget; + this.input.init(); + } + return this; + }, + + /** + * stop recognizing for this session. + * This session will be discarded, when a new [input]start event is fired. + * When forced, the recognizer cycle is stopped immediately. + * @param {Boolean} [force] + */ + stop: function(force) { + this.session.stopped = force ? FORCED_STOP : STOP; + }, + + /** + * run the recognizers! + * called by the inputHandler function on every movement of the pointers (touches) + * it walks through all the recognizers and tries to detect the gesture that is being made + * @param {Object} inputData + */ + recognize: function(inputData) { + var session = this.session; + if (session.stopped) { + return; + } + + // run the touch-action polyfill + this.touchAction.preventDefaults(inputData); + + var recognizer; + var recognizers = this.recognizers; + + // this holds the recognizer that is being recognized. + // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED + // if no recognizer is detecting a thing, it is set to `null` + var curRecognizer = session.curRecognizer; + + // reset when the last recognizer is recognized + // or when we're in a new session + if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { + curRecognizer = session.curRecognizer = null; + } + + var i = 0; + while (i < recognizers.length) { + recognizer = recognizers[i]; + + // find out if we are allowed try to recognize the input for this one. + // 1. allow if the session is NOT forced stopped (see the .stop() method) + // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one + // that is being recognized. + // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. + // this can be setup with the `recognizeWith()` method on the recognizer. + if (session.stopped !== FORCED_STOP && ( // 1 + !curRecognizer || recognizer == curRecognizer || // 2 + recognizer.canRecognizeWith(curRecognizer))) { // 3 + recognizer.recognize(inputData); + } else { + recognizer.reset(); + } + + // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the + // current active recognizer. but only if we don't already have an active recognizer + if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { + curRecognizer = session.curRecognizer = recognizer; + } + i++; + } + }, + + /** + * get a recognizer by its event name. + * @param {Recognizer|String} recognizer + * @returns {Recognizer|Null} + */ + get: function(recognizer) { + if (recognizer instanceof Recognizer) { + return recognizer; + } + + var recognizers = this.recognizers; + for (var i = 0; i < recognizers.length; i++) { + if (recognizers[i].options.event == recognizer) { + return recognizers[i]; + } + } + return null; + }, + + /** + * add a recognizer to the manager + * existing recognizers with the same event name will be removed + * @param {Recognizer} recognizer + * @returns {Recognizer|Manager} + */ + add: function(recognizer) { + if (invokeArrayArg(recognizer, 'add', this)) { + return this; + } + + // remove existing + var existing = this.get(recognizer.options.event); + if (existing) { + this.remove(existing); + } + + this.recognizers.push(recognizer); + recognizer.manager = this; + + this.touchAction.update(); + return recognizer; + }, + + /** + * remove a recognizer by name or instance + * @param {Recognizer|String} recognizer + * @returns {Manager} + */ + remove: function(recognizer) { + if (invokeArrayArg(recognizer, 'remove', this)) { + return this; + } + + var recognizers = this.recognizers; + recognizer = this.get(recognizer); + recognizers.splice(inArray(recognizers, recognizer), 1); + + this.touchAction.update(); + return this; + }, + + /** + * bind event + * @param {String} events + * @param {Function} handler + * @returns {EventEmitter} this + */ + on: function(events, handler) { + var handlers = this.handlers; + each(splitStr(events), function(event) { + handlers[event] = handlers[event] || []; + handlers[event].push(handler); + }); + return this; + }, + + /** + * unbind event, leave emit blank to remove all handlers + * @param {String} events + * @param {Function} [handler] + * @returns {EventEmitter} this + */ + off: function(events, handler) { + var handlers = this.handlers; + each(splitStr(events), function(event) { + if (!handler) { + delete handlers[event]; + } else { + handlers[event].splice(inArray(handlers[event], handler), 1); + } + }); + return this; + }, + + /** + * emit event to the listeners + * @param {String} event + * @param {Object} data + */ + emit: function(event, data) { + // we also want to trigger dom events + if (this.options.domEvents) { + triggerDomEvent(event, data); + } + + // no handlers, so skip it all + var handlers = this.handlers[event] && this.handlers[event].slice(); + if (!handlers || !handlers.length) { + return; + } + + data.type = event; + data.preventDefault = function() { + data.srcEvent.preventDefault(); + }; + + var i = 0; + while (i < handlers.length) { + handlers[i](data); + i++; + } + }, + + /** + * destroy the manager and unbinds all events + * it doesn't unbind dom events, that is the user own responsibility + */ + destroy: function() { + this.element && toggleCssProps(this, false); + + this.handlers = {}; + this.session = {}; + this.input.destroy(); + this.element = null; + } + }; + + /** + * add/remove the css properties as defined in manager.options.cssProps + * @param {Manager} manager + * @param {Boolean} add + */ + function toggleCssProps(manager, add) { + var element = manager.element; + each(manager.options.cssProps, function(value, name) { + element.style[prefixed(element.style, name)] = add ? value : ''; + }); + } + + /** + * trigger dom event + * @param {String} event + * @param {Object} data + */ + function triggerDomEvent(event, data) { + var gestureEvent = document.createEvent('Event'); + gestureEvent.initEvent(event, true, true); + gestureEvent.gesture = data; + data.target.dispatchEvent(gestureEvent); + } + + extend(Hammer, { + INPUT_START: INPUT_START, + INPUT_MOVE: INPUT_MOVE, + INPUT_END: INPUT_END, + INPUT_CANCEL: INPUT_CANCEL, + + STATE_POSSIBLE: STATE_POSSIBLE, + STATE_BEGAN: STATE_BEGAN, + STATE_CHANGED: STATE_CHANGED, + STATE_ENDED: STATE_ENDED, + STATE_RECOGNIZED: STATE_RECOGNIZED, + STATE_CANCELLED: STATE_CANCELLED, + STATE_FAILED: STATE_FAILED, + + DIRECTION_NONE: DIRECTION_NONE, + DIRECTION_LEFT: DIRECTION_LEFT, + DIRECTION_RIGHT: DIRECTION_RIGHT, + DIRECTION_UP: DIRECTION_UP, + DIRECTION_DOWN: DIRECTION_DOWN, + DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, + DIRECTION_VERTICAL: DIRECTION_VERTICAL, + DIRECTION_ALL: DIRECTION_ALL, + + Manager: Manager, + Input: Input, + TouchAction: TouchAction, + + TouchInput: TouchInput, + MouseInput: MouseInput, + PointerEventInput: PointerEventInput, + TouchMouseInput: TouchMouseInput, + SingleTouchInput: SingleTouchInput, + + Recognizer: Recognizer, + AttrRecognizer: AttrRecognizer, + Tap: TapRecognizer, + Pan: PanRecognizer, + Swipe: SwipeRecognizer, + Pinch: PinchRecognizer, + Rotate: RotateRecognizer, + Press: PressRecognizer, + + on: addEventListeners, + off: removeEventListeners, + each: each, + merge: merge, + extend: extend, + inherit: inherit, + bindFn: bindFn, + prefixed: prefixed + }); + + if ("function" == TYPE_FUNCTION && __webpack_require__(23)) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { + return Hammer; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof module != 'undefined' && module.exports) { + module.exports = Hammer; + } else { + window[exportName] = Hammer; + } + + })(window, document, 'Hammer'); - var TYPE_FUNCTION = 'function'; - var round = Math.round; - var abs = Math.abs; - var now = Date.now; +/***/ }, +/* 23 */ +/***/ function(module, exports) { - /** - * set a timeout with a given scope - * @param {Function} fn - * @param {Number} timeout - * @param {Object} context - * @returns {number} - */ - function setTimeoutContext(fn, timeout, context) { - return setTimeout(bindFn(fn, context), timeout); - } + /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; - /** - * if the argument is an array, we want to execute the fn on each entry - * if it aint an array we don't want to do a thing. - * this is used by all the methods that accept a single and array argument. - * @param {*|Array} arg - * @param {String} fn - * @param {Object} [context] - * @returns {Boolean} - */ - function invokeArrayArg(arg, fn, context) { - if (Array.isArray(arg)) { - each(arg, context[fn], context); - return true; - } - return false; - } + /* WEBPACK VAR INJECTION */}.call(exports, {})) - /** - * walk objects and arrays - * @param {Object} obj - * @param {Function} iterator - * @param {Object} context - */ - function each(obj, iterator, context) { - var i; +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { - if (!obj) { - return; - } + 'use strict'; - if (obj.forEach) { - obj.forEach(iterator, context); - } else if (obj.length !== undefined) { - i = 0; - while (i < obj.length) { - iterator.call(context, obj[i], i, obj); - i++; - } - } else { - for (i in obj) { - obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); - } - } - } + var util = __webpack_require__(1); + var hammerUtil = __webpack_require__(25); + var moment = __webpack_require__(2); + var Component = __webpack_require__(26); + var DateUtil = __webpack_require__(27); /** - * extend object. - * means that properties in dest will be overwritten by the ones in src. - * @param {Object} dest - * @param {Object} src - * @param {Boolean} [merge] - * @returns {Object} dest + * @constructor Range + * A Range controls a numeric range with a start and end value. + * The Range adjusts the range based on mouse events or programmatic changes, + * and triggers events when the range is changing or has been changed. + * @param {{dom: Object, domProps: Object, emitter: Emitter}} body + * @param {Object} [options] See description at Range.setOptions */ - function extend(dest, src, merge) { - var keys = Object.keys(src); - var i = 0; - while (i < keys.length) { - if (!merge || (merge && dest[keys[i]] === undefined)) { - dest[keys[i]] = src[keys[i]]; - } - i++; - } - return dest; - } + function Range(body, options) { + var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); + this.start = now.clone().add(-3, 'days').valueOf(); // Number + this.end = now.clone().add(4, 'days').valueOf(); // Number - /** - * merge the values from src in the dest. - * means that properties that exist in dest will not be overwritten by src - * @param {Object} dest - * @param {Object} src - * @returns {Object} dest - */ - function merge(dest, src) { - return extend(dest, src, true); - } + this.body = body; + this.deltaDifference = 0; + this.scaleOffset = 0; + this.startToFront = false; + this.endToFront = true; - /** - * simple class inheritance - * @param {Function} child - * @param {Function} base - * @param {Object} [properties] - */ - function inherit(child, base, properties) { - var baseP = base.prototype, - childP; + // default options + this.defaultOptions = { + start: null, + end: null, + moment: moment, + direction: 'horizontal', // 'horizontal' or 'vertical' + moveable: true, + zoomable: true, + min: null, + max: null, + zoomMin: 10, // milliseconds + zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds + }; + this.options = util.extend({}, this.defaultOptions); - childP = child.prototype = Object.create(baseP); - childP.constructor = child; - childP._super = baseP; + this.props = { + touch: {} + }; + this.animationTimer = null; - if (properties) { - extend(childP, properties); - } - } + // drag listeners for dragging + this.body.emitter.on('panstart', this._onDragStart.bind(this)); + this.body.emitter.on('panmove', this._onDrag.bind(this)); + this.body.emitter.on('panend', this._onDragEnd.bind(this)); - /** - * simple function bind - * @param {Function} fn - * @param {Object} context - * @returns {Function} - */ - function bindFn(fn, context) { - return function boundFn() { - return fn.apply(context, arguments); - }; - } + // mouse wheel for zooming + this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); - /** - * let a boolean value also be a function that must return a boolean - * this first item in args will be used as the context - * @param {Boolean|Function} val - * @param {Array} [args] - * @returns {Boolean} - */ - function boolOrFn(val, args) { - if (typeof val == TYPE_FUNCTION) { - return val.apply(args ? args[0] || undefined : undefined, args); - } - return val; - } + // pinch to zoom + this.body.emitter.on('touch', this._onTouch.bind(this)); + this.body.emitter.on('pinch', this._onPinch.bind(this)); - /** - * use the val2 when val1 is undefined - * @param {*} val1 - * @param {*} val2 - * @returns {*} - */ - function ifUndefined(val1, val2) { - return (val1 === undefined) ? val2 : val1; + this.setOptions(options); } - /** - * addEventListener with multiple events at once - * @param {EventTarget} target - * @param {String} types - * @param {Function} handler - */ - function addEventListeners(target, types, handler) { - each(splitStr(types), function(type) { - target.addEventListener(type, handler, false); - }); - } + Range.prototype = new Component(); /** - * removeEventListener with multiple events at once - * @param {EventTarget} target - * @param {String} types - * @param {Function} handler + * Set options for the range controller + * @param {Object} options Available options: + * {Number | Date | String} start Start date for the range + * {Number | Date | String} end End date for the range + * {Number} min Minimum value for start + * {Number} max Maximum value for end + * {Number} zoomMin Set a minimum value for + * (end - start). + * {Number} zoomMax Set a maximum value for + * (end - start). + * {Boolean} moveable Enable moving of the range + * by dragging. True by default + * {Boolean} zoomable Enable zooming of the range + * by pinching/scrolling. True by default */ - function removeEventListeners(target, types, handler) { - each(splitStr(types), function(type) { - target.removeEventListener(type, handler, false); - }); - } + Range.prototype.setOptions = function (options) { + if (options) { + // copy the options that we know + var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey']; + util.selectiveExtend(fields, this.options, options); - /** - * find if a node is in the given parent - * @method hasParent - * @param {HTMLElement} node - * @param {HTMLElement} parent - * @return {Boolean} found - */ - function hasParent(node, parent) { - while (node) { - if (node == parent) { - return true; - } - node = node.parentNode; + if ('start' in options || 'end' in options) { + // apply a new range. both start and end are optional + this.setRange(options.start, options.end); } - return false; - } + } + }; /** - * small indexOf wrapper - * @param {String} str - * @param {String} find - * @returns {Boolean} found + * Test whether direction has a valid value + * @param {String} direction 'horizontal' or 'vertical' */ - function inStr(str, find) { - return str.indexOf(find) > -1; + function validateDirection(direction) { + if (direction != 'horizontal' && direction != 'vertical') { + throw new TypeError('Unknown direction "' + direction + '". ' + 'Choose "horizontal" or "vertical".'); + } } /** - * split string on whitespace - * @param {String} str - * @returns {Array} words + * Set a new start and end range + * @param {Date | Number | String} [start] + * @param {Date | Number | String} [end] + * @param {boolean | {duration: number, easingFunction: string}} [animation=false] + * If true (default), the range is animated + * smoothly to the new window. An object can be + * provided to specify duration and easing function. + * Default duration is 500 ms, and default easing + * function is 'easeInOutQuad'. + * @param {Boolean} [byUser=false] + * */ - function splitStr(str) { - return str.trim().split(/\s+/g); - } + Range.prototype.setRange = function (start, end, animation, byUser) { + if (byUser !== true) { + byUser = false; + } + var finalStart = start != undefined ? util.convert(start, 'Date').valueOf() : null; + var finalEnd = end != undefined ? util.convert(end, 'Date').valueOf() : null; + this._cancelAnimation(); - /** - * find if a array contains the object using indexOf or a simple polyFill - * @param {Array} src - * @param {String} find - * @param {String} [findByKey] - * @return {Boolean|Number} false when not found, or the index - */ - function inArray(src, find, findByKey) { - if (src.indexOf && !findByKey) { - return src.indexOf(find); - } else { - var i = 0; - while (i < src.length) { - if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { - return i; - } - i++; - } - return -1; + if (animation) { + // true or an Object + var me = this; + var initStart = this.start; + var initEnd = this.end; + var duration = typeof animation === 'object' && 'duration' in animation ? animation.duration : 500; + var easingName = typeof animation === 'object' && 'easingFunction' in animation ? animation.easingFunction : 'easeInOutQuad'; + var easingFunction = util.easingFunctions[easingName]; + if (!easingFunction) { + throw new Error('Unknown easing function ' + JSON.stringify(easingName) + '. ' + 'Choose from: ' + Object.keys(util.easingFunctions).join(', ')); } - } - /** - * convert array-like objects to real arrays - * @param {Object} obj - * @returns {Array} - */ - function toArray(obj) { - return Array.prototype.slice.call(obj, 0); - } + var initTime = new Date().valueOf(); + var anyChanged = false; - /** - * unique array with objects based on a key (like 'id') or just by the array's value - * @param {Array} src [{id:1},{id:2},{id:1}] - * @param {String} [key] - * @param {Boolean} [sort=False] - * @returns {Array} [{id:1},{id:2}] - */ - function uniqueArray(src, key, sort) { - var results = []; - var values = []; - var i = 0; + var next = function next() { + if (!me.props.touch.dragging) { + var now = new Date().valueOf(); + var time = now - initTime; + var ease = easingFunction(time / duration); + var done = time > duration; + var s = done || finalStart === null ? finalStart : initStart + (finalStart - initStart) * ease; + var e = done || finalEnd === null ? finalEnd : initEnd + (finalEnd - initEnd) * ease; - while (i < src.length) { - var val = key ? src[i][key] : src[i]; - if (inArray(values, val) < 0) { - results.push(src[i]); + changed = me._applyRange(s, e); + DateUtil.updateHiddenDates(me.options.moment, me.body, me.options.hiddenDates); + anyChanged = anyChanged || changed; + if (changed) { + me.body.emitter.emit('rangechange', { start: new Date(me.start), end: new Date(me.end), byUser: byUser }); } - values[i] = val; - i++; - } - if (sort) { - if (!key) { - results = results.sort(); + if (done) { + if (anyChanged) { + me.body.emitter.emit('rangechanged', { start: new Date(me.start), end: new Date(me.end), byUser: byUser }); + } } else { - results = results.sort(function sortUniqueArray(a, b) { - return a[key] > b[key]; - }); + // animate with as high as possible frame rate, leave 20 ms in between + // each to prevent the browser from blocking + me.animationTimer = setTimeout(next, 20); } - } - - return results; - } - - /** - * get the prefixed property - * @param {Object} obj - * @param {String} property - * @returns {String|Undefined} prefixed - */ - function prefixed(obj, property) { - var prefix, prop; - var camelProp = property[0].toUpperCase() + property.slice(1); - - var i = 0; - while (i < VENDOR_PREFIXES.length) { - prefix = VENDOR_PREFIXES[i]; - prop = (prefix) ? prefix + camelProp : property; + } + }; - if (prop in obj) { - return prop; - } - i++; + return next(); + } else { + var changed = this._applyRange(finalStart, finalEnd); + DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates); + if (changed) { + var params = { start: new Date(this.start), end: new Date(this.end), byUser: byUser }; + this.body.emitter.emit('rangechange', params); + this.body.emitter.emit('rangechanged', params); } - return undefined; - } + } + }; /** - * get a unique id - * @returns {number} uniqueId + * Stop an animation + * @private */ - var _uniqueId = 1; - function uniqueId() { - return _uniqueId++; - } + Range.prototype._cancelAnimation = function () { + if (this.animationTimer) { + clearTimeout(this.animationTimer); + this.animationTimer = null; + } + }; /** - * get the window object of an element - * @param {HTMLElement} element - * @returns {DocumentView|Window} + * Set a new start and end range. This method is the same as setRange, but + * does not trigger a range change and range changed event, and it returns + * true when the range is changed + * @param {Number} [start] + * @param {Number} [end] + * @return {Boolean} changed + * @private */ - function getWindowForElement(element) { - var doc = element.ownerDocument; - return (doc.defaultView || doc.parentWindow); - } - - var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; - - var SUPPORT_TOUCH = ('ontouchstart' in window); - var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; - var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); - - var INPUT_TYPE_TOUCH = 'touch'; - var INPUT_TYPE_PEN = 'pen'; - var INPUT_TYPE_MOUSE = 'mouse'; - var INPUT_TYPE_KINECT = 'kinect'; - - var COMPUTE_INTERVAL = 25; + Range.prototype._applyRange = function (start, end) { + var newStart = start != null ? util.convert(start, 'Date').valueOf() : this.start, + newEnd = end != null ? util.convert(end, 'Date').valueOf() : this.end, + max = this.options.max != null ? util.convert(this.options.max, 'Date').valueOf() : null, + min = this.options.min != null ? util.convert(this.options.min, 'Date').valueOf() : null, + diff; - var INPUT_START = 1; - var INPUT_MOVE = 2; - var INPUT_END = 4; - var INPUT_CANCEL = 8; + // check for valid number + if (isNaN(newStart) || newStart === null) { + throw new Error('Invalid start "' + start + '"'); + } + if (isNaN(newEnd) || newEnd === null) { + throw new Error('Invalid end "' + end + '"'); + } - var DIRECTION_NONE = 1; - var DIRECTION_LEFT = 2; - var DIRECTION_RIGHT = 4; - var DIRECTION_UP = 8; - var DIRECTION_DOWN = 16; + // prevent start < end + if (newEnd < newStart) { + newEnd = newStart; + } - var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; - var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; - var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; + // prevent start < min + if (min !== null) { + if (newStart < min) { + diff = min - newStart; + newStart += diff; + newEnd += diff; - var PROPS_XY = ['x', 'y']; - var PROPS_CLIENT_XY = ['clientX', 'clientY']; + // prevent end > max + if (max != null) { + if (newEnd > max) { + newEnd = max; + } + } + } + } - /** - * create new input type manager - * @param {Manager} manager - * @param {Function} callback - * @returns {Input} - * @constructor - */ - function Input(manager, callback) { - var self = this; - this.manager = manager; - this.callback = callback; - this.element = manager.element; - this.target = manager.options.inputTarget; + // prevent end > max + if (max !== null) { + if (newEnd > max) { + diff = newEnd - max; + newStart -= diff; + newEnd -= diff; - // smaller wrapper around the handler, for the scope and the enabled state of the manager, - // so when disabled the input events are completely bypassed. - this.domHandler = function(ev) { - if (boolOrFn(manager.options.enable, [manager])) { - self.handler(ev); + // prevent start < min + if (min != null) { + if (newStart < min) { + newStart = min; } - }; + } + } + } - this.init(); + // prevent (end-start) < zoomMin + if (this.options.zoomMin !== null) { + var zoomMin = parseFloat(this.options.zoomMin); + if (zoomMin < 0) { + zoomMin = 0; + } + if (newEnd - newStart < zoomMin) { + if (this.end - this.start === zoomMin && newStart > this.start && newEnd < this.end) { + // ignore this action, we are already zoomed to the minimum + newStart = this.start; + newEnd = this.end; + } else { + // zoom to the minimum + diff = zoomMin - (newEnd - newStart); + newStart -= diff / 2; + newEnd += diff / 2; + } + } + } - } + // prevent (end-start) > zoomMax + if (this.options.zoomMax !== null) { + var zoomMax = parseFloat(this.options.zoomMax); + if (zoomMax < 0) { + zoomMax = 0; + } - Input.prototype = { - /** - * should handle the inputEvent data and trigger the callback - * @virtual - */ - handler: function() { }, + if (newEnd - newStart > zoomMax) { + if (this.end - this.start === zoomMax && newStart < this.start && newEnd > this.end) { + // ignore this action, we are already zoomed to the maximum + newStart = this.start; + newEnd = this.end; + } else { + // zoom to the maximum + diff = newEnd - newStart - zoomMax; + newStart += diff / 2; + newEnd -= diff / 2; + } + } + } - /** - * bind the events - */ - init: function() { - this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); - this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); - this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); - }, + var changed = this.start != newStart || this.end != newEnd; - /** - * unbind the events - */ - destroy: function() { - this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); - this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); - this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); - } + // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not necessarily of type Range) + if (!(newStart >= this.start && newStart <= this.end || newEnd >= this.start && newEnd <= this.end) && !(this.start >= newStart && this.start <= newEnd || this.end >= newStart && this.end <= newEnd)) { + this.body.emitter.emit('checkRangedItems'); + } + + this.start = newStart; + this.end = newEnd; + return changed; }; /** - * create new input type manager - * called by the Manager constructor - * @param {Hammer} manager - * @returns {Input} + * Retrieve the current range. + * @return {Object} An object with start and end properties */ - function createInputInstance(manager) { - var Type; - var inputClass = manager.options.inputClass; + Range.prototype.getRange = function () { + return { + start: this.start, + end: this.end + }; + }; - if (inputClass) { - Type = inputClass; - } else if (SUPPORT_POINTER_EVENTS) { - Type = PointerEventInput; - } else if (SUPPORT_ONLY_TOUCH) { - Type = TouchInput; - } else if (!SUPPORT_TOUCH) { - Type = MouseInput; - } else { - Type = TouchMouseInput; - } - return new (Type)(manager, inputHandler); - } + /** + * Calculate the conversion offset and scale for current range, based on + * the provided width + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion + */ + Range.prototype.conversion = function (width, totalHidden) { + return Range.conversion(this.start, this.end, width, totalHidden); + }; /** - * handle input events - * @param {Manager} manager - * @param {String} eventType - * @param {Object} input + * Static method to calculate the conversion offset and scale for a range, + * based on the provided start, end, and width + * @param {Number} start + * @param {Number} end + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion */ - function inputHandler(manager, eventType, input) { - var pointersLen = input.pointers.length; - var changedPointersLen = input.changedPointers.length; - var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); - var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); + Range.conversion = function (start, end, width, totalHidden) { + if (totalHidden === undefined) { + totalHidden = 0; + } + if (width != 0 && end - start != 0) { + return { + offset: start, + scale: width / (end - start - totalHidden) + }; + } else { + return { + offset: 0, + scale: 1 + }; + } + }; - input.isFirst = !!isFirst; - input.isFinal = !!isFinal; + /** + * Start dragging horizontally or vertically + * @param {Event} event + * @private + */ + Range.prototype._onDragStart = function (event) { + this.deltaDifference = 0; + this.previousDelta = 0; - if (isFirst) { - manager.session = {}; - } + // only allow dragging when configured as movable + if (!this.options.moveable) return; - // source event is the normalized value of the domEvents - // like 'touchstart, mouseup, pointerdown' - input.eventType = eventType; + // only start dragging when the mouse is inside the current range + if (!this._isInsideRange(event)) return; - // compute scale, rotation etc - computeInputData(manager, input); + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - // emit secret event - manager.emit('hammer.input', input); + this.props.touch.start = this.start; + this.props.touch.end = this.end; + this.props.touch.dragging = true; - manager.recognize(input); - manager.session.prevInput = input; - } + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'move'; + } + }; /** - * extend the data with some usable properties like scale, rotate, velocity etc - * @param {Object} manager - * @param {Object} input + * Perform dragging operation + * @param {Event} event + * @private */ - function computeInputData(manager, input) { - var session = manager.session; - var pointers = input.pointers; - var pointersLength = pointers.length; - - // store the first input to calculate the distance and direction - if (!session.firstInput) { - session.firstInput = simpleCloneInputData(input); - } + Range.prototype._onDrag = function (event) { + if (!this.props.touch.dragging) return; - // to compute scale and rotation we need to store the multiple touches - if (pointersLength > 1 && !session.firstMultiple) { - session.firstMultiple = simpleCloneInputData(input); - } else if (pointersLength === 1) { - session.firstMultiple = false; - } + // only allow dragging when configured as movable + if (!this.options.moveable) return; - var firstInput = session.firstInput; - var firstMultiple = session.firstMultiple; - var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; + // TODO: this may be redundant in hammerjs2 + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - var center = input.center = getCenter(pointers); - input.timeStamp = now(); - input.deltaTime = input.timeStamp - firstInput.timeStamp; + var direction = this.options.direction; + validateDirection(direction); + var delta = direction == 'horizontal' ? event.deltaX : event.deltaY; + delta -= this.deltaDifference; + var interval = this.props.touch.end - this.props.touch.start; - input.angle = getAngle(offsetCenter, center); - input.distance = getDistance(offsetCenter, center); + // normalize dragging speed if cutout is in between. + var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + interval -= duration; - computeDeltaXY(session, input); - input.offsetDirection = getDirection(input.deltaX, input.deltaY); - - input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; - input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; - - computeIntervalInputData(session, input); - - // find the correct target - var target = manager.element; - if (hasParent(input.srcEvent.target, target)) { - target = input.srcEvent.target; - } - input.target = target; - } - - function computeDeltaXY(session, input) { - var center = input.center; - var offset = session.offsetDelta || {}; - var prevDelta = session.prevDelta || {}; - var prevInput = session.prevInput || {}; - - if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { - prevDelta = session.prevDelta = { - x: prevInput.deltaX || 0, - y: prevInput.deltaY || 0 - }; - - offset = session.offsetDelta = { - x: center.x, - y: center.y - }; - } - - input.deltaX = prevDelta.x + (center.x - offset.x); - input.deltaY = prevDelta.y + (center.y - offset.y); - } - - /** - * velocity is calculated every x ms - * @param {Object} session - * @param {Object} input - */ - function computeIntervalInputData(session, input) { - var last = session.lastInterval || input, - deltaTime = input.timeStamp - last.timeStamp, - velocity, velocityX, velocityY, direction; - - if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { - var deltaX = last.deltaX - input.deltaX; - var deltaY = last.deltaY - input.deltaY; - - var v = getVelocity(deltaTime, deltaX, deltaY); - velocityX = v.x; - velocityY = v.y; - velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; - direction = getDirection(deltaX, deltaY); - - session.lastInterval = input; - } else { - // use latest velocity info if it doesn't overtake a minimum period - velocity = last.velocity; - velocityX = last.velocityX; - velocityY = last.velocityY; - direction = last.direction; - } - - input.velocity = velocity; - input.velocityX = velocityX; - input.velocityY = velocityY; - input.direction = direction; - } - - /** - * create a simple clone from the input used for storage of firstInput and firstMultiple - * @param {Object} input - * @returns {Object} clonedInputData - */ - function simpleCloneInputData(input) { - // make a simple copy of the pointers because we will get a reference if we don't - // we only need clientXY for the calculations - var pointers = []; - var i = 0; - while (i < input.pointers.length) { - pointers[i] = { - clientX: round(input.pointers[i].clientX), - clientY: round(input.pointers[i].clientY) - }; - i++; - } - - return { - timeStamp: now(), - pointers: pointers, - center: getCenter(pointers), - deltaX: input.deltaX, - deltaY: input.deltaY - }; - } - - /** - * get the center of all the pointers - * @param {Array} pointers - * @return {Object} center contains `x` and `y` properties - */ - function getCenter(pointers) { - var pointersLength = pointers.length; - - // no need to loop when only one touch - if (pointersLength === 1) { - return { - x: round(pointers[0].clientX), - y: round(pointers[0].clientY) - }; - } - - var x = 0, y = 0, i = 0; - while (i < pointersLength) { - x += pointers[i].clientX; - y += pointers[i].clientY; - i++; - } - - return { - x: round(x / pointersLength), - y: round(y / pointersLength) - }; - } - - /** - * calculate the velocity between two points. unit is in px per ms. - * @param {Number} deltaTime - * @param {Number} x - * @param {Number} y - * @return {Object} velocity `x` and `y` - */ - function getVelocity(deltaTime, x, y) { - return { - x: x / deltaTime || 0, - y: y / deltaTime || 0 - }; - } - - /** - * get the direction between two points - * @param {Number} x - * @param {Number} y - * @return {Number} direction - */ - function getDirection(x, y) { - if (x === y) { - return DIRECTION_NONE; - } - - if (abs(x) >= abs(y)) { - return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - return y > 0 ? DIRECTION_UP : DIRECTION_DOWN; - } - - /** - * calculate the absolute distance between two points - * @param {Object} p1 {x, y} - * @param {Object} p2 {x, y} - * @param {Array} [props] containing x and y keys - * @return {Number} distance - */ - function getDistance(p1, p2, props) { - if (!props) { - props = PROPS_XY; - } - var x = p2[props[0]] - p1[props[0]], - y = p2[props[1]] - p1[props[1]]; - - return Math.sqrt((x * x) + (y * y)); - } - - /** - * calculate the angle between two coordinates - * @param {Object} p1 - * @param {Object} p2 - * @param {Array} [props] containing x and y keys - * @return {Number} angle - */ - function getAngle(p1, p2, props) { - if (!props) { - props = PROPS_XY; - } - var x = p2[props[0]] - p1[props[0]], - y = p2[props[1]] - p1[props[1]]; - return Math.atan2(y, x) * 180 / Math.PI; - } - - /** - * calculate the rotation degrees between two pointersets - * @param {Array} start array of pointers - * @param {Array} end array of pointers - * @return {Number} rotation - */ - function getRotation(start, end) { - return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY); - } - - /** - * calculate the scale factor between two pointersets - * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out - * @param {Array} start array of pointers - * @param {Array} end array of pointers - * @return {Number} scale - */ - function getScale(start, end) { - return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); - } - - var MOUSE_INPUT_MAP = { - mousedown: INPUT_START, - mousemove: INPUT_MOVE, - mouseup: INPUT_END - }; - - var MOUSE_ELEMENT_EVENTS = 'mousedown'; - var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; - - /** - * Mouse events input - * @constructor - * @extends Input - */ - function MouseInput() { - this.evEl = MOUSE_ELEMENT_EVENTS; - this.evWin = MOUSE_WINDOW_EVENTS; - - this.allow = true; // used by Input.TouchMouse to disable mouse events - this.pressed = false; // mousedown state - - Input.apply(this, arguments); - } - - inherit(MouseInput, Input, { - /** - * handle mouse events - * @param {Object} ev - */ - handler: function MEhandler(ev) { - var eventType = MOUSE_INPUT_MAP[ev.type]; - - // on start we want to have the left mouse button down - if (eventType & INPUT_START && ev.button === 0) { - this.pressed = true; - } - - if (eventType & INPUT_MOVE && ev.which !== 1) { - eventType = INPUT_END; - } - - // mouse must be down, and mouse events are allowed (see the TouchMouse input) - if (!this.pressed || !this.allow) { - return; - } - - if (eventType & INPUT_END) { - this.pressed = false; - } - - this.callback(this.manager, eventType, { - pointers: [ev], - changedPointers: [ev], - pointerType: INPUT_TYPE_MOUSE, - srcEvent: ev - }); - } - }); - - var POINTER_INPUT_MAP = { - pointerdown: INPUT_START, - pointermove: INPUT_MOVE, - pointerup: INPUT_END, - pointercancel: INPUT_CANCEL, - pointerout: INPUT_CANCEL - }; - - // in IE10 the pointer types is defined as an enum - var IE10_POINTER_TYPE_ENUM = { - 2: INPUT_TYPE_TOUCH, - 3: INPUT_TYPE_PEN, - 4: INPUT_TYPE_MOUSE, - 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 - }; - - var POINTER_ELEMENT_EVENTS = 'pointerdown'; - var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; - - // IE10 has prefixed support, and case-sensitive - if (window.MSPointerEvent) { - POINTER_ELEMENT_EVENTS = 'MSPointerDown'; - POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; - } - - /** - * Pointer events input - * @constructor - * @extends Input - */ - function PointerEventInput() { - this.evEl = POINTER_ELEMENT_EVENTS; - this.evWin = POINTER_WINDOW_EVENTS; - - Input.apply(this, arguments); - - this.store = (this.manager.session.pointerEvents = []); - } - - inherit(PointerEventInput, Input, { - /** - * handle mouse events - * @param {Object} ev - */ - handler: function PEhandler(ev) { - var store = this.store; - var removePointer = false; - - var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); - var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; - var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; - - var isTouch = (pointerType == INPUT_TYPE_TOUCH); - - // get index of the event in the store - var storeIndex = inArray(store, ev.pointerId, 'pointerId'); - - // start and mouse must be down - if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { - if (storeIndex < 0) { - store.push(ev); - storeIndex = store.length - 1; - } - } else if (eventType & (INPUT_END | INPUT_CANCEL)) { - removePointer = true; - } - - // it not found, so the pointer hasn't been down (so it's probably a hover) - if (storeIndex < 0) { - return; - } - - // update the event in the store - store[storeIndex] = ev; - - this.callback(this.manager, eventType, { - pointers: store, - changedPointers: [ev], - pointerType: pointerType, - srcEvent: ev - }); - - if (removePointer) { - // remove from the store - store.splice(storeIndex, 1); - } - } - }); - - var SINGLE_TOUCH_INPUT_MAP = { - touchstart: INPUT_START, - touchmove: INPUT_MOVE, - touchend: INPUT_END, - touchcancel: INPUT_CANCEL - }; - - var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; - var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; - - /** - * Touch events input - * @constructor - * @extends Input - */ - function SingleTouchInput() { - this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; - this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; - this.started = false; - - Input.apply(this, arguments); - } - - inherit(SingleTouchInput, Input, { - handler: function TEhandler(ev) { - var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; - - // should we handle the touch events? - if (type === INPUT_START) { - this.started = true; - } - - if (!this.started) { - return; - } - - var touches = normalizeSingleTouches.call(this, ev, type); - - // when done, reset the started state - if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { - this.started = false; - } - - this.callback(this.manager, type, { - pointers: touches[0], - changedPointers: touches[1], - pointerType: INPUT_TYPE_TOUCH, - srcEvent: ev - }); - } - }); - - /** - * @this {TouchInput} - * @param {Object} ev - * @param {Number} type flag - * @returns {undefined|Array} [all, changed] - */ - function normalizeSingleTouches(ev, type) { - var all = toArray(ev.touches); - var changed = toArray(ev.changedTouches); - - if (type & (INPUT_END | INPUT_CANCEL)) { - all = uniqueArray(all.concat(changed), 'identifier', true); - } - - return [all, changed]; - } - - var TOUCH_INPUT_MAP = { - touchstart: INPUT_START, - touchmove: INPUT_MOVE, - touchend: INPUT_END, - touchcancel: INPUT_CANCEL - }; - - var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; - - /** - * Multi-user touch events input - * @constructor - * @extends Input - */ - function TouchInput() { - this.evTarget = TOUCH_TARGET_EVENTS; - this.targetIds = {}; - - Input.apply(this, arguments); - } - - inherit(TouchInput, Input, { - handler: function MTEhandler(ev) { - var type = TOUCH_INPUT_MAP[ev.type]; - var touches = getTouches.call(this, ev, type); - if (!touches) { - return; - } - - this.callback(this.manager, type, { - pointers: touches[0], - changedPointers: touches[1], - pointerType: INPUT_TYPE_TOUCH, - srcEvent: ev - }); - } - }); - - /** - * @this {TouchInput} - * @param {Object} ev - * @param {Number} type flag - * @returns {undefined|Array} [all, changed] - */ - function getTouches(ev, type) { - var allTouches = toArray(ev.touches); - var targetIds = this.targetIds; - - // when there is only one touch, the process can be simplified - if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { - targetIds[allTouches[0].identifier] = true; - return [allTouches, allTouches]; - } - - var i, - targetTouches, - changedTouches = toArray(ev.changedTouches), - changedTargetTouches = [], - target = this.target; - - // get target touches from touches - targetTouches = allTouches.filter(function(touch) { - return hasParent(touch.target, target); - }); - - // collect touches - if (type === INPUT_START) { - i = 0; - while (i < targetTouches.length) { - targetIds[targetTouches[i].identifier] = true; - i++; - } - } - - // filter changed touches to only contain touches that exist in the collected target ids - i = 0; - while (i < changedTouches.length) { - if (targetIds[changedTouches[i].identifier]) { - changedTargetTouches.push(changedTouches[i]); - } - - // cleanup removed touches - if (type & (INPUT_END | INPUT_CANCEL)) { - delete targetIds[changedTouches[i].identifier]; - } - i++; - } - - if (!changedTargetTouches.length) { - return; - } - - return [ - // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' - uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), - changedTargetTouches - ]; - } - - /** - * Combined touch and mouse input - * - * Touch has a higher priority then mouse, and while touching no mouse events are allowed. - * This because touch devices also emit mouse events while doing a touch. - * - * @constructor - * @extends Input - */ - function TouchMouseInput() { - Input.apply(this, arguments); - - var handler = bindFn(this.handler, this); - this.touch = new TouchInput(this.manager, handler); - this.mouse = new MouseInput(this.manager, handler); - } - - inherit(TouchMouseInput, Input, { - /** - * handle mouse and touch events - * @param {Hammer} manager - * @param {String} inputEvent - * @param {Object} inputData - */ - handler: function TMEhandler(manager, inputEvent, inputData) { - var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), - isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); - - // when we're in a touch event, so block all upcoming mouse events - // most mobile browser also emit mouseevents, right after touchstart - if (isTouch) { - this.mouse.allow = false; - } else if (isMouse && !this.mouse.allow) { - return; - } - - // reset the allowMouse when we're done - if (inputEvent & (INPUT_END | INPUT_CANCEL)) { - this.mouse.allow = true; - } - - this.callback(manager, inputEvent, inputData); - }, - - /** - * remove the event listeners - */ - destroy: function destroy() { - this.touch.destroy(); - this.mouse.destroy(); - } - }); - - var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); - var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; - - // magical touchAction value - var TOUCH_ACTION_COMPUTE = 'compute'; - var TOUCH_ACTION_AUTO = 'auto'; - var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented - var TOUCH_ACTION_NONE = 'none'; - var TOUCH_ACTION_PAN_X = 'pan-x'; - var TOUCH_ACTION_PAN_Y = 'pan-y'; - - /** - * Touch Action - * sets the touchAction property or uses the js alternative - * @param {Manager} manager - * @param {String} value - * @constructor - */ - function TouchAction(manager, value) { - this.manager = manager; - this.set(value); - } - - TouchAction.prototype = { - /** - * set the touchAction value on the element or enable the polyfill - * @param {String} value - */ - set: function(value) { - // find out the touch-action by the event handlers - if (value == TOUCH_ACTION_COMPUTE) { - value = this.compute(); - } - - if (NATIVE_TOUCH_ACTION) { - this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; - } - this.actions = value.toLowerCase().trim(); - }, - - /** - * just re-set the touchAction value - */ - update: function() { - this.set(this.manager.options.touchAction); - }, - - /** - * compute the value for the touchAction property based on the recognizer's settings - * @returns {String} value - */ - compute: function() { - var actions = []; - each(this.manager.recognizers, function(recognizer) { - if (boolOrFn(recognizer.options.enable, [recognizer])) { - actions = actions.concat(recognizer.getTouchAction()); - } - }); - return cleanTouchActions(actions.join(' ')); - }, - - /** - * this method is called on each input cycle and provides the preventing of the browser behavior - * @param {Object} input - */ - preventDefaults: function(input) { - // not needed with native support for the touchAction property - if (NATIVE_TOUCH_ACTION) { - return; - } - - var srcEvent = input.srcEvent; - var direction = input.offsetDirection; - - // if the touch action did prevented once this session - if (this.manager.session.prevented) { - srcEvent.preventDefault(); - return; - } - - var actions = this.actions; - var hasNone = inStr(actions, TOUCH_ACTION_NONE); - var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); - var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); - - if (hasNone || - (hasPanY && direction & DIRECTION_HORIZONTAL) || - (hasPanX && direction & DIRECTION_VERTICAL)) { - return this.preventSrc(srcEvent); - } - }, - - /** - * call preventDefault to prevent the browser's default behavior (scrolling in most cases) - * @param {Object} srcEvent - */ - preventSrc: function(srcEvent) { - this.manager.session.prevented = true; - srcEvent.preventDefault(); - } - }; - - /** - * when the touchActions are collected they are not a valid value, so we need to clean things up. * - * @param {String} actions - * @returns {*} - */ - function cleanTouchActions(actions) { - // none - if (inStr(actions, TOUCH_ACTION_NONE)) { - return TOUCH_ACTION_NONE; - } - - var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); - var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); - - // pan-x and pan-y can be combined - if (hasPanX && hasPanY) { - return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y; - } - - // pan-x OR pan-y - if (hasPanX || hasPanY) { - return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; - } - - // manipulation - if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { - return TOUCH_ACTION_MANIPULATION; - } - - return TOUCH_ACTION_AUTO; - } - - /** - * Recognizer flow explained; * - * All recognizers have the initial state of POSSIBLE when a input session starts. - * The definition of a input session is from the first input until the last input, with all it's movement in it. * - * Example session for mouse-input: mousedown -> mousemove -> mouseup - * - * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed - * which determines with state it should be. - * - * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to - * POSSIBLE to give it another change on the next cycle. - * - * Possible - * | - * +-----+---------------+ - * | | - * +-----+-----+ | - * | | | - * Failed Cancelled | - * +-------+------+ - * | | - * Recognized Began - * | - * Changed - * | - * Ended/Recognized - */ - var STATE_POSSIBLE = 1; - var STATE_BEGAN = 2; - var STATE_CHANGED = 4; - var STATE_ENDED = 8; - var STATE_RECOGNIZED = STATE_ENDED; - var STATE_CANCELLED = 16; - var STATE_FAILED = 32; - - /** - * Recognizer - * Every recognizer needs to extend from this class. - * @constructor - * @param {Object} options - */ - function Recognizer(options) { - this.id = uniqueId(); - - this.manager = null; - this.options = merge(options || {}, this.defaults); - - // default is enable true - this.options.enable = ifUndefined(this.options.enable, true); - - this.state = STATE_POSSIBLE; - - this.simultaneous = {}; - this.requireFail = []; - } - - Recognizer.prototype = { - /** - * @virtual - * @type {Object} - */ - defaults: {}, - - /** - * set options - * @param {Object} options - * @return {Recognizer} - */ - set: function(options) { - extend(this.options, options); - - // also update the touchAction, in case something changed about the directions/enabled state - this.manager && this.manager.touchAction.update(); - return this; - }, - - /** - * recognize simultaneous with an other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - recognizeWith: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { - return this; - } - - var simultaneous = this.simultaneous; - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - if (!simultaneous[otherRecognizer.id]) { - simultaneous[otherRecognizer.id] = otherRecognizer; - otherRecognizer.recognizeWith(this); - } - return this; - }, - - /** - * drop the simultaneous link. it doesnt remove the link on the other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - dropRecognizeWith: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { - return this; - } - - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - delete this.simultaneous[otherRecognizer.id]; - return this; - }, - - /** - * recognizer can only run when an other is failing - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - requireFailure: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { - return this; - } - - var requireFail = this.requireFail; - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - if (inArray(requireFail, otherRecognizer) === -1) { - requireFail.push(otherRecognizer); - otherRecognizer.requireFailure(this); - } - return this; - }, - - /** - * drop the requireFailure link. it does not remove the link on the other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - dropRequireFailure: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { - return this; - } - - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - var index = inArray(this.requireFail, otherRecognizer); - if (index > -1) { - this.requireFail.splice(index, 1); - } - return this; - }, - - /** - * has require failures boolean - * @returns {boolean} - */ - hasRequireFailures: function() { - return this.requireFail.length > 0; - }, - - /** - * if the recognizer can recognize simultaneous with an other recognizer - * @param {Recognizer} otherRecognizer - * @returns {Boolean} - */ - canRecognizeWith: function(otherRecognizer) { - return !!this.simultaneous[otherRecognizer.id]; - }, - - /** - * You should use `tryEmit` instead of `emit` directly to check - * that all the needed recognizers has failed before emitting. - * @param {Object} input - */ - emit: function(input) { - var self = this; - var state = this.state; - - function emit(withState) { - self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input); - } - - // 'panstart' and 'panmove' - if (state < STATE_ENDED) { - emit(true); - } - - emit(); // simple 'eventName' events - - // panend and pancancel - if (state >= STATE_ENDED) { - emit(true); - } - }, - - /** - * Check that all the require failure recognizers has failed, - * if true, it emits a gesture event, - * otherwise, setup the state to FAILED. - * @param {Object} input - */ - tryEmit: function(input) { - if (this.canEmit()) { - return this.emit(input); - } - // it's failing anyway - this.state = STATE_FAILED; - }, - - /** - * can we emit? - * @returns {boolean} - */ - canEmit: function() { - var i = 0; - while (i < this.requireFail.length) { - if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { - return false; - } - i++; - } - return true; - }, - - /** - * update the recognizer - * @param {Object} inputData - */ - recognize: function(inputData) { - // make a new copy of the inputData - // so we can change the inputData without messing up the other recognizers - var inputDataClone = extend({}, inputData); - - // is is enabled and allow recognizing? - if (!boolOrFn(this.options.enable, [this, inputDataClone])) { - this.reset(); - this.state = STATE_FAILED; - return; - } - - // reset when we've reached the end - if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { - this.state = STATE_POSSIBLE; - } - - this.state = this.process(inputDataClone); - - // the recognizer has recognized a gesture - // so trigger an event - if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { - this.tryEmit(inputDataClone); - } - }, - - /** - * return the state of the recognizer - * the actual recognizing happens in this method - * @virtual - * @param {Object} inputData - * @returns {Const} STATE - */ - process: function(inputData) { }, // jshint ignore:line - - /** - * return the preferred touch-action - * @virtual - * @returns {Array} - */ - getTouchAction: function() { }, - - /** - * called when the gesture isn't allowed to recognize - * like when another is being recognized or it is disabled - * @virtual - */ - reset: function() { } - }; - - /** - * get a usable string, used as event postfix - * @param {Const} state - * @returns {String} state - */ - function stateStr(state) { - if (state & STATE_CANCELLED) { - return 'cancel'; - } else if (state & STATE_ENDED) { - return 'end'; - } else if (state & STATE_CHANGED) { - return 'move'; - } else if (state & STATE_BEGAN) { - return 'start'; - } - return ''; - } - - /** - * direction cons to string - * @param {Const} direction - * @returns {String} - */ - function directionStr(direction) { - if (direction == DIRECTION_DOWN) { - return 'down'; - } else if (direction == DIRECTION_UP) { - return 'up'; - } else if (direction == DIRECTION_LEFT) { - return 'left'; - } else if (direction == DIRECTION_RIGHT) { - return 'right'; - } - return ''; - } - - /** - * get a recognizer by name if it is bound to a manager - * @param {Recognizer|String} otherRecognizer - * @param {Recognizer} recognizer - * @returns {Recognizer} - */ - function getRecognizerByNameIfManager(otherRecognizer, recognizer) { - var manager = recognizer.manager; - if (manager) { - return manager.get(otherRecognizer); - } - return otherRecognizer; - } - - /** - * This recognizer is just used as a base for the simple attribute recognizers. - * @constructor - * @extends Recognizer - */ - function AttrRecognizer() { - Recognizer.apply(this, arguments); - } - - inherit(AttrRecognizer, Recognizer, { - /** - * @namespace - * @memberof AttrRecognizer - */ - defaults: { - /** - * @type {Number} - * @default 1 - */ - pointers: 1 - }, - - /** - * Used to check if it the recognizer receives valid input, like input.distance > 10. - * @memberof AttrRecognizer - * @param {Object} input - * @returns {Boolean} recognized - */ - attrTest: function(input) { - var optionPointers = this.options.pointers; - return optionPointers === 0 || input.pointers.length === optionPointers; - }, - - /** - * Process the input and return the state for the recognizer - * @memberof AttrRecognizer - * @param {Object} input - * @returns {*} State - */ - process: function(input) { - var state = this.state; - var eventType = input.eventType; - - var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); - var isValid = this.attrTest(input); - - // on cancel input and we've recognized before, return STATE_CANCELLED - if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { - return state | STATE_CANCELLED; - } else if (isRecognized || isValid) { - if (eventType & INPUT_END) { - return state | STATE_ENDED; - } else if (!(state & STATE_BEGAN)) { - return STATE_BEGAN; - } - return state | STATE_CHANGED; - } - return STATE_FAILED; - } - }); - - /** - * Pan - * Recognized when the pointer is down and moved in the allowed direction. - * @constructor - * @extends AttrRecognizer - */ - function PanRecognizer() { - AttrRecognizer.apply(this, arguments); - - this.pX = null; - this.pY = null; - } - - inherit(PanRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof PanRecognizer - */ - defaults: { - event: 'pan', - threshold: 10, - pointers: 1, - direction: DIRECTION_ALL - }, - - getTouchAction: function() { - var direction = this.options.direction; - var actions = []; - if (direction & DIRECTION_HORIZONTAL) { - actions.push(TOUCH_ACTION_PAN_Y); - } - if (direction & DIRECTION_VERTICAL) { - actions.push(TOUCH_ACTION_PAN_X); - } - return actions; - }, - - directionTest: function(input) { - var options = this.options; - var hasMoved = true; - var distance = input.distance; - var direction = input.direction; - var x = input.deltaX; - var y = input.deltaY; - - // lock to axis? - if (!(direction & options.direction)) { - if (options.direction & DIRECTION_HORIZONTAL) { - direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; - hasMoved = x != this.pX; - distance = Math.abs(input.deltaX); - } else { - direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; - hasMoved = y != this.pY; - distance = Math.abs(input.deltaY); - } - } - input.direction = direction; - return hasMoved && distance > options.threshold && direction & options.direction; - }, - - attrTest: function(input) { - return AttrRecognizer.prototype.attrTest.call(this, input) && - (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); - }, - - emit: function(input) { - this.pX = input.deltaX; - this.pY = input.deltaY; - - var direction = directionStr(input.direction); - if (direction) { - this.manager.emit(this.options.event + direction, input); - } - - this._super.emit.call(this, input); - } - }); - - /** - * Pinch - * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). - * @constructor - * @extends AttrRecognizer - */ - function PinchRecognizer() { - AttrRecognizer.apply(this, arguments); - } - - inherit(PinchRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof PinchRecognizer - */ - defaults: { - event: 'pinch', - threshold: 0, - pointers: 2 - }, - - getTouchAction: function() { - return [TOUCH_ACTION_NONE]; - }, - - attrTest: function(input) { - return this._super.attrTest.call(this, input) && - (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); - }, - - emit: function(input) { - this._super.emit.call(this, input); - if (input.scale !== 1) { - var inOut = input.scale < 1 ? 'in' : 'out'; - this.manager.emit(this.options.event + inOut, input); - } - } - }); - - /** - * Press - * Recognized when the pointer is down for x ms without any movement. - * @constructor - * @extends Recognizer - */ - function PressRecognizer() { - Recognizer.apply(this, arguments); - - this._timer = null; - this._input = null; - } - - inherit(PressRecognizer, Recognizer, { - /** - * @namespace - * @memberof PressRecognizer - */ - defaults: { - event: 'press', - pointers: 1, - time: 500, // minimal time of the pointer to be pressed - threshold: 5 // a minimal movement is ok, but keep it low - }, - - getTouchAction: function() { - return [TOUCH_ACTION_AUTO]; - }, - - process: function(input) { - var options = this.options; - var validPointers = input.pointers.length === options.pointers; - var validMovement = input.distance < options.threshold; - var validTime = input.deltaTime > options.time; - - this._input = input; - - // we only allow little movement - // and we've reached an end event, so a tap is possible - if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { - this.reset(); - } else if (input.eventType & INPUT_START) { - this.reset(); - this._timer = setTimeoutContext(function() { - this.state = STATE_RECOGNIZED; - this.tryEmit(); - }, options.time, this); - } else if (input.eventType & INPUT_END) { - return STATE_RECOGNIZED; - } - return STATE_FAILED; - }, - - reset: function() { - clearTimeout(this._timer); - }, - - emit: function(input) { - if (this.state !== STATE_RECOGNIZED) { - return; - } - - if (input && (input.eventType & INPUT_END)) { - this.manager.emit(this.options.event + 'up', input); - } else { - this._input.timeStamp = now(); - this.manager.emit(this.options.event, this._input); - } - } - }); - - /** - * Rotate - * Recognized when two or more pointer are moving in a circular motion. - * @constructor - * @extends AttrRecognizer - */ - function RotateRecognizer() { - AttrRecognizer.apply(this, arguments); - } - - inherit(RotateRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof RotateRecognizer - */ - defaults: { - event: 'rotate', - threshold: 0, - pointers: 2 - }, - - getTouchAction: function() { - return [TOUCH_ACTION_NONE]; - }, - - attrTest: function(input) { - return this._super.attrTest.call(this, input) && - (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); - } - }); - - /** - * Swipe - * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. - * @constructor - * @extends AttrRecognizer - */ - function SwipeRecognizer() { - AttrRecognizer.apply(this, arguments); - } - - inherit(SwipeRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof SwipeRecognizer - */ - defaults: { - event: 'swipe', - threshold: 10, - velocity: 0.65, - direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, - pointers: 1 - }, - - getTouchAction: function() { - return PanRecognizer.prototype.getTouchAction.call(this); - }, - - attrTest: function(input) { - var direction = this.options.direction; - var velocity; - - if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { - velocity = input.velocity; - } else if (direction & DIRECTION_HORIZONTAL) { - velocity = input.velocityX; - } else if (direction & DIRECTION_VERTICAL) { - velocity = input.velocityY; - } - - return this._super.attrTest.call(this, input) && - direction & input.direction && - input.distance > this.options.threshold && - abs(velocity) > this.options.velocity && input.eventType & INPUT_END; - }, - - emit: function(input) { - var direction = directionStr(input.direction); - if (direction) { - this.manager.emit(this.options.event + direction, input); - } - - this.manager.emit(this.options.event, input); - } - }); - - /** - * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur - * between the given interval and position. The delay option can be used to recognize multi-taps without firing - * a single tap. - * - * The eventData from the emitted event contains the property `tapCount`, which contains the amount of - * multi-taps being recognized. - * @constructor - * @extends Recognizer - */ - function TapRecognizer() { - Recognizer.apply(this, arguments); - - // previous time and center, - // used for tap counting - this.pTime = false; - this.pCenter = false; - - this._timer = null; - this._input = null; - this.count = 0; - } - - inherit(TapRecognizer, Recognizer, { - /** - * @namespace - * @memberof PinchRecognizer - */ - defaults: { - event: 'tap', - pointers: 1, - taps: 1, - interval: 300, // max time between the multi-tap taps - time: 250, // max time of the pointer to be down (like finger on the screen) - threshold: 2, // a minimal movement is ok, but keep it low - posThreshold: 10 // a multi-tap can be a bit off the initial position - }, - - getTouchAction: function() { - return [TOUCH_ACTION_MANIPULATION]; - }, - - process: function(input) { - var options = this.options; - - var validPointers = input.pointers.length === options.pointers; - var validMovement = input.distance < options.threshold; - var validTouchTime = input.deltaTime < options.time; - - this.reset(); - - if ((input.eventType & INPUT_START) && (this.count === 0)) { - return this.failTimeout(); - } - - // we only allow little movement - // and we've reached an end event, so a tap is possible - if (validMovement && validTouchTime && validPointers) { - if (input.eventType != INPUT_END) { - return this.failTimeout(); - } - - var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; - var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; - - this.pTime = input.timeStamp; - this.pCenter = input.center; - - if (!validMultiTap || !validInterval) { - this.count = 1; - } else { - this.count += 1; - } - - this._input = input; - - // if tap count matches we have recognized it, - // else it has began recognizing... - var tapCount = this.count % options.taps; - if (tapCount === 0) { - // no failing requirements, immediately trigger the tap event - // or wait as long as the multitap interval to trigger - if (!this.hasRequireFailures()) { - return STATE_RECOGNIZED; - } else { - this._timer = setTimeoutContext(function() { - this.state = STATE_RECOGNIZED; - this.tryEmit(); - }, options.interval, this); - return STATE_BEGAN; - } - } - } - return STATE_FAILED; - }, - - failTimeout: function() { - this._timer = setTimeoutContext(function() { - this.state = STATE_FAILED; - }, this.options.interval, this); - return STATE_FAILED; - }, - - reset: function() { - clearTimeout(this._timer); - }, - - emit: function() { - if (this.state == STATE_RECOGNIZED ) { - this._input.tapCount = this.count; - this.manager.emit(this.options.event, this._input); - } - } - }); - - /** - * Simple way to create an manager with a default set of recognizers. - * @param {HTMLElement} element - * @param {Object} [options] - * @constructor - */ - function Hammer(element, options) { - options = options || {}; - options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); - return new Manager(element, options); - } - - /** - * @const {string} - */ - Hammer.VERSION = '2.0.4'; - - /** - * default settings - * @namespace - */ - Hammer.defaults = { - /** - * set if DOM events are being triggered. - * But this is slower and unused by simple implementations, so disabled by default. - * @type {Boolean} - * @default false - */ - domEvents: false, - - /** - * The value for the touchAction property/fallback. - * When set to `compute` it will magically set the correct value based on the added recognizers. - * @type {String} - * @default compute - */ - touchAction: TOUCH_ACTION_COMPUTE, - - /** - * @type {Boolean} - * @default true - */ - enable: true, - - /** - * EXPERIMENTAL FEATURE -- can be removed/changed - * Change the parent input target element. - * If Null, then it is being set the to main element. - * @type {Null|EventTarget} - * @default null - */ - inputTarget: null, - - /** - * force an input class - * @type {Null|Function} - * @default null - */ - inputClass: null, - - /** - * Default recognizer setup when calling `Hammer()` - * When creating a new Manager these will be skipped. - * @type {Array} - */ - preset: [ - // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] - [RotateRecognizer, { enable: false }], - [PinchRecognizer, { enable: false }, ['rotate']], - [SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }], - [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], - [TapRecognizer], - [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], - [PressRecognizer] - ], - - /** - * Some CSS properties can be used to improve the working of Hammer. - * Add them to this method and they will be set when creating a new Manager. - * @namespace - */ - cssProps: { - /** - * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. - * @type {String} - * @default 'none' - */ - userSelect: 'none', - - /** - * Disable the Windows Phone grippers when pressing an element. - * @type {String} - * @default 'none' - */ - touchSelect: 'none', - - /** - * Disables the default callout shown when you touch and hold a touch target. - * On iOS, when you touch and hold a touch target such as a link, Safari displays - * a callout containing information about the link. This property allows you to disable that callout. - * @type {String} - * @default 'none' - */ - touchCallout: 'none', - - /** - * Specifies whether zooming is enabled. Used by IE10> - * @type {String} - * @default 'none' - */ - contentZooming: 'none', - - /** - * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. - * @type {String} - * @default 'none' - */ - userDrag: 'none', - - /** - * Overrides the highlight color shown when the user taps a link or a JavaScript - * clickable element in iOS. This property obeys the alpha value, if specified. - * @type {String} - * @default 'rgba(0,0,0,0)' - */ - tapHighlightColor: 'rgba(0,0,0,0)' - } - }; - - var STOP = 1; - var FORCED_STOP = 2; - - /** - * Manager - * @param {HTMLElement} element - * @param {Object} [options] - * @constructor - */ - function Manager(element, options) { - options = options || {}; - - this.options = merge(options, Hammer.defaults); - this.options.inputTarget = this.options.inputTarget || element; - - this.handlers = {}; - this.session = {}; - this.recognizers = []; - - this.element = element; - this.input = createInputInstance(this); - this.touchAction = new TouchAction(this, this.options.touchAction); - - toggleCssProps(this, true); - - each(options.recognizers, function(item) { - var recognizer = this.add(new (item[0])(item[1])); - item[2] && recognizer.recognizeWith(item[2]); - item[3] && recognizer.requireFailure(item[3]); - }, this); - } - - Manager.prototype = { - /** - * set options - * @param {Object} options - * @returns {Manager} - */ - set: function(options) { - extend(this.options, options); - - // Options that need a little more setup - if (options.touchAction) { - this.touchAction.update(); - } - if (options.inputTarget) { - // Clean up existing event listeners and reinitialize - this.input.destroy(); - this.input.target = options.inputTarget; - this.input.init(); - } - return this; - }, - - /** - * stop recognizing for this session. - * This session will be discarded, when a new [input]start event is fired. - * When forced, the recognizer cycle is stopped immediately. - * @param {Boolean} [force] - */ - stop: function(force) { - this.session.stopped = force ? FORCED_STOP : STOP; - }, - - /** - * run the recognizers! - * called by the inputHandler function on every movement of the pointers (touches) - * it walks through all the recognizers and tries to detect the gesture that is being made - * @param {Object} inputData - */ - recognize: function(inputData) { - var session = this.session; - if (session.stopped) { - return; - } - - // run the touch-action polyfill - this.touchAction.preventDefaults(inputData); - - var recognizer; - var recognizers = this.recognizers; - - // this holds the recognizer that is being recognized. - // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED - // if no recognizer is detecting a thing, it is set to `null` - var curRecognizer = session.curRecognizer; - - // reset when the last recognizer is recognized - // or when we're in a new session - if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { - curRecognizer = session.curRecognizer = null; - } - - var i = 0; - while (i < recognizers.length) { - recognizer = recognizers[i]; - - // find out if we are allowed try to recognize the input for this one. - // 1. allow if the session is NOT forced stopped (see the .stop() method) - // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one - // that is being recognized. - // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. - // this can be setup with the `recognizeWith()` method on the recognizer. - if (session.stopped !== FORCED_STOP && ( // 1 - !curRecognizer || recognizer == curRecognizer || // 2 - recognizer.canRecognizeWith(curRecognizer))) { // 3 - recognizer.recognize(inputData); - } else { - recognizer.reset(); - } - - // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the - // current active recognizer. but only if we don't already have an active recognizer - if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { - curRecognizer = session.curRecognizer = recognizer; - } - i++; - } - }, - - /** - * get a recognizer by its event name. - * @param {Recognizer|String} recognizer - * @returns {Recognizer|Null} - */ - get: function(recognizer) { - if (recognizer instanceof Recognizer) { - return recognizer; - } - - var recognizers = this.recognizers; - for (var i = 0; i < recognizers.length; i++) { - if (recognizers[i].options.event == recognizer) { - return recognizers[i]; - } - } - return null; - }, - - /** - * add a recognizer to the manager - * existing recognizers with the same event name will be removed - * @param {Recognizer} recognizer - * @returns {Recognizer|Manager} - */ - add: function(recognizer) { - if (invokeArrayArg(recognizer, 'add', this)) { - return this; - } - - // remove existing - var existing = this.get(recognizer.options.event); - if (existing) { - this.remove(existing); - } - - this.recognizers.push(recognizer); - recognizer.manager = this; - - this.touchAction.update(); - return recognizer; - }, - - /** - * remove a recognizer by name or instance - * @param {Recognizer|String} recognizer - * @returns {Manager} - */ - remove: function(recognizer) { - if (invokeArrayArg(recognizer, 'remove', this)) { - return this; - } - - var recognizers = this.recognizers; - recognizer = this.get(recognizer); - recognizers.splice(inArray(recognizers, recognizer), 1); - - this.touchAction.update(); - return this; - }, - - /** - * bind event - * @param {String} events - * @param {Function} handler - * @returns {EventEmitter} this - */ - on: function(events, handler) { - var handlers = this.handlers; - each(splitStr(events), function(event) { - handlers[event] = handlers[event] || []; - handlers[event].push(handler); - }); - return this; - }, - - /** - * unbind event, leave emit blank to remove all handlers - * @param {String} events - * @param {Function} [handler] - * @returns {EventEmitter} this - */ - off: function(events, handler) { - var handlers = this.handlers; - each(splitStr(events), function(event) { - if (!handler) { - delete handlers[event]; - } else { - handlers[event].splice(inArray(handlers[event], handler), 1); - } - }); - return this; - }, - - /** - * emit event to the listeners - * @param {String} event - * @param {Object} data - */ - emit: function(event, data) { - // we also want to trigger dom events - if (this.options.domEvents) { - triggerDomEvent(event, data); - } - - // no handlers, so skip it all - var handlers = this.handlers[event] && this.handlers[event].slice(); - if (!handlers || !handlers.length) { - return; - } - - data.type = event; - data.preventDefault = function() { - data.srcEvent.preventDefault(); - }; - - var i = 0; - while (i < handlers.length) { - handlers[i](data); - i++; - } - }, - - /** - * destroy the manager and unbinds all events - * it doesn't unbind dom events, that is the user own responsibility - */ - destroy: function() { - this.element && toggleCssProps(this, false); - - this.handlers = {}; - this.session = {}; - this.input.destroy(); - this.element = null; - } - }; - - /** - * add/remove the css properties as defined in manager.options.cssProps - * @param {Manager} manager - * @param {Boolean} add - */ - function toggleCssProps(manager, add) { - var element = manager.element; - each(manager.options.cssProps, function(value, name) { - element.style[prefixed(element.style, name)] = add ? value : ''; - }); - } - - /** - * trigger dom event - * @param {String} event - * @param {Object} data - */ - function triggerDomEvent(event, data) { - var gestureEvent = document.createEvent('Event'); - gestureEvent.initEvent(event, true, true); - gestureEvent.gesture = data; - data.target.dispatchEvent(gestureEvent); - } - - extend(Hammer, { - INPUT_START: INPUT_START, - INPUT_MOVE: INPUT_MOVE, - INPUT_END: INPUT_END, - INPUT_CANCEL: INPUT_CANCEL, - - STATE_POSSIBLE: STATE_POSSIBLE, - STATE_BEGAN: STATE_BEGAN, - STATE_CHANGED: STATE_CHANGED, - STATE_ENDED: STATE_ENDED, - STATE_RECOGNIZED: STATE_RECOGNIZED, - STATE_CANCELLED: STATE_CANCELLED, - STATE_FAILED: STATE_FAILED, - - DIRECTION_NONE: DIRECTION_NONE, - DIRECTION_LEFT: DIRECTION_LEFT, - DIRECTION_RIGHT: DIRECTION_RIGHT, - DIRECTION_UP: DIRECTION_UP, - DIRECTION_DOWN: DIRECTION_DOWN, - DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, - DIRECTION_VERTICAL: DIRECTION_VERTICAL, - DIRECTION_ALL: DIRECTION_ALL, - - Manager: Manager, - Input: Input, - TouchAction: TouchAction, - - TouchInput: TouchInput, - MouseInput: MouseInput, - PointerEventInput: PointerEventInput, - TouchMouseInput: TouchMouseInput, - SingleTouchInput: SingleTouchInput, - - Recognizer: Recognizer, - AttrRecognizer: AttrRecognizer, - Tap: TapRecognizer, - Pan: PanRecognizer, - Swipe: SwipeRecognizer, - Pinch: PinchRecognizer, - Rotate: RotateRecognizer, - Press: PressRecognizer, - - on: addEventListeners, - off: removeEventListeners, - each: each, - merge: merge, - extend: extend, - inherit: inherit, - bindFn: bindFn, - prefixed: prefixed - }); - - if ("function" == TYPE_FUNCTION && __webpack_require__(23)) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { - return Hammer; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof module != 'undefined' && module.exports) { - module.exports = Hammer; - } else { - window[exportName] = Hammer; - } - - })(window, document, 'Hammer'); - - -/***/ }, -/* 23 */ -/***/ function(module, exports) { - - /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; - - /* WEBPACK VAR INJECTION */}.call(exports, {})) - -/***/ }, -/* 24 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(25); - var moment = __webpack_require__(2); - var Component = __webpack_require__(26); - var DateUtil = __webpack_require__(27); - - /** - * @constructor Range - * A Range controls a numeric range with a start and end value. - * The Range adjusts the range based on mouse events or programmatic changes, - * and triggers events when the range is changing or has been changed. - * @param {{dom: Object, domProps: Object, emitter: Emitter}} body - * @param {Object} [options] See description at Range.setOptions - */ - function Range(body, options) { - var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); - this.start = now.clone().add(-3, 'days').valueOf(); // Number - this.end = now.clone().add(4, 'days').valueOf(); // Number - - this.body = body; - this.deltaDifference = 0; - this.scaleOffset = 0; - this.startToFront = false; - this.endToFront = true; - - // default options - this.defaultOptions = { - start: null, - end: null, - moment: moment, - direction: 'horizontal', // 'horizontal' or 'vertical' - moveable: true, - zoomable: true, - min: null, - max: null, - zoomMin: 10, // milliseconds - zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds - }; - this.options = util.extend({}, this.defaultOptions); - - this.props = { - touch: {} - }; - this.animationTimer = null; - - // drag listeners for dragging - this.body.emitter.on('panstart', this._onDragStart.bind(this)); - this.body.emitter.on('panmove', this._onDrag.bind(this)); - this.body.emitter.on('panend', this._onDragEnd.bind(this)); - - // mouse wheel for zooming - this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); - - // pinch to zoom - this.body.emitter.on('touch', this._onTouch.bind(this)); - this.body.emitter.on('pinch', this._onPinch.bind(this)); - - this.setOptions(options); - } - - Range.prototype = new Component(); - - /** - * Set options for the range controller - * @param {Object} options Available options: - * {Number | Date | String} start Start date for the range - * {Number | Date | String} end End date for the range - * {Number} min Minimum value for start - * {Number} max Maximum value for end - * {Number} zoomMin Set a minimum value for - * (end - start). - * {Number} zoomMax Set a maximum value for - * (end - start). - * {Boolean} moveable Enable moving of the range - * by dragging. True by default - * {Boolean} zoomable Enable zooming of the range - * by pinching/scrolling. True by default - */ - Range.prototype.setOptions = function (options) { - if (options) { - // copy the options that we know - var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey']; - util.selectiveExtend(fields, this.options, options); - - if ('start' in options || 'end' in options) { - // apply a new range. both start and end are optional - this.setRange(options.start, options.end); - } - } - }; - - /** - * Test whether direction has a valid value - * @param {String} direction 'horizontal' or 'vertical' - */ - function validateDirection(direction) { - if (direction != 'horizontal' && direction != 'vertical') { - throw new TypeError('Unknown direction "' + direction + '". ' + 'Choose "horizontal" or "vertical".'); - } - } - - /** - * Set a new start and end range - * @param {Date | Number | String} [start] - * @param {Date | Number | String} [end] - * @param {boolean | {duration: number, easingFunction: string}} [animation=false] - * If true (default), the range is animated - * smoothly to the new window. An object can be - * provided to specify duration and easing function. - * Default duration is 500 ms, and default easing - * function is 'easeInOutQuad'. - * @param {Boolean} [byUser=false] - * - */ - Range.prototype.setRange = function (start, end, animation, byUser) { - if (byUser !== true) { - byUser = false; - } - var finalStart = start != undefined ? util.convert(start, 'Date').valueOf() : null; - var finalEnd = end != undefined ? util.convert(end, 'Date').valueOf() : null; - this._cancelAnimation(); - - if (animation) { - // true or an Object - var me = this; - var initStart = this.start; - var initEnd = this.end; - var duration = typeof animation === 'object' && 'duration' in animation ? animation.duration : 500; - var easingName = typeof animation === 'object' && 'easingFunction' in animation ? animation.easingFunction : 'easeInOutQuad'; - var easingFunction = util.easingFunctions[easingName]; - if (!easingFunction) { - throw new Error('Unknown easing function ' + JSON.stringify(easingName) + '. ' + 'Choose from: ' + Object.keys(util.easingFunctions).join(', ')); - } - - var initTime = new Date().valueOf(); - var anyChanged = false; - - var next = function next() { - if (!me.props.touch.dragging) { - var now = new Date().valueOf(); - var time = now - initTime; - var ease = easingFunction(time / duration); - var done = time > duration; - var s = done || finalStart === null ? finalStart : initStart + (finalStart - initStart) * ease; - var e = done || finalEnd === null ? finalEnd : initEnd + (finalEnd - initEnd) * ease; - - changed = me._applyRange(s, e); - DateUtil.updateHiddenDates(me.options.moment, me.body, me.options.hiddenDates); - anyChanged = anyChanged || changed; - if (changed) { - me.body.emitter.emit('rangechange', { start: new Date(me.start), end: new Date(me.end), byUser: byUser }); - } - - if (done) { - if (anyChanged) { - me.body.emitter.emit('rangechanged', { start: new Date(me.start), end: new Date(me.end), byUser: byUser }); - } - } else { - // animate with as high as possible frame rate, leave 20 ms in between - // each to prevent the browser from blocking - me.animationTimer = setTimeout(next, 20); - } - } - }; - - return next(); - } else { - var changed = this._applyRange(finalStart, finalEnd); - DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates); - if (changed) { - var params = { start: new Date(this.start), end: new Date(this.end), byUser: byUser }; - this.body.emitter.emit('rangechange', params); - this.body.emitter.emit('rangechanged', params); - } - } - }; - - /** - * Stop an animation - * @private - */ - Range.prototype._cancelAnimation = function () { - if (this.animationTimer) { - clearTimeout(this.animationTimer); - this.animationTimer = null; - } - }; - - /** - * Set a new start and end range. This method is the same as setRange, but - * does not trigger a range change and range changed event, and it returns - * true when the range is changed - * @param {Number} [start] - * @param {Number} [end] - * @return {Boolean} changed - * @private - */ - Range.prototype._applyRange = function (start, end) { - var newStart = start != null ? util.convert(start, 'Date').valueOf() : this.start, - newEnd = end != null ? util.convert(end, 'Date').valueOf() : this.end, - max = this.options.max != null ? util.convert(this.options.max, 'Date').valueOf() : null, - min = this.options.min != null ? util.convert(this.options.min, 'Date').valueOf() : null, - diff; - - // check for valid number - if (isNaN(newStart) || newStart === null) { - throw new Error('Invalid start "' + start + '"'); - } - if (isNaN(newEnd) || newEnd === null) { - throw new Error('Invalid end "' + end + '"'); - } - - // prevent start < end - if (newEnd < newStart) { - newEnd = newStart; - } - - // prevent start < min - if (min !== null) { - if (newStart < min) { - diff = min - newStart; - newStart += diff; - newEnd += diff; - - // prevent end > max - if (max != null) { - if (newEnd > max) { - newEnd = max; - } - } - } - } - - // prevent end > max - if (max !== null) { - if (newEnd > max) { - diff = newEnd - max; - newStart -= diff; - newEnd -= diff; - - // prevent start < min - if (min != null) { - if (newStart < min) { - newStart = min; - } - } - } - } - - // prevent (end-start) < zoomMin - if (this.options.zoomMin !== null) { - var zoomMin = parseFloat(this.options.zoomMin); - if (zoomMin < 0) { - zoomMin = 0; - } - if (newEnd - newStart < zoomMin) { - if (this.end - this.start === zoomMin && newStart > this.start && newEnd < this.end) { - // ignore this action, we are already zoomed to the minimum - newStart = this.start; - newEnd = this.end; - } else { - // zoom to the minimum - diff = zoomMin - (newEnd - newStart); - newStart -= diff / 2; - newEnd += diff / 2; - } - } - } - - // prevent (end-start) > zoomMax - if (this.options.zoomMax !== null) { - var zoomMax = parseFloat(this.options.zoomMax); - if (zoomMax < 0) { - zoomMax = 0; - } - - if (newEnd - newStart > zoomMax) { - if (this.end - this.start === zoomMax && newStart < this.start && newEnd > this.end) { - // ignore this action, we are already zoomed to the maximum - newStart = this.start; - newEnd = this.end; - } else { - // zoom to the maximum - diff = newEnd - newStart - zoomMax; - newStart += diff / 2; - newEnd -= diff / 2; - } - } - } - - var changed = this.start != newStart || this.end != newEnd; - - // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not necessarily of type Range) - if (!(newStart >= this.start && newStart <= this.end || newEnd >= this.start && newEnd <= this.end) && !(this.start >= newStart && this.start <= newEnd || this.end >= newStart && this.end <= newEnd)) { - this.body.emitter.emit('checkRangedItems'); - } - - this.start = newStart; - this.end = newEnd; - return changed; - }; - - /** - * Retrieve the current range. - * @return {Object} An object with start and end properties - */ - Range.prototype.getRange = function () { - return { - start: this.start, - end: this.end - }; - }; - - /** - * Calculate the conversion offset and scale for current range, based on - * the provided width - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion - */ - Range.prototype.conversion = function (width, totalHidden) { - return Range.conversion(this.start, this.end, width, totalHidden); - }; - - /** - * Static method to calculate the conversion offset and scale for a range, - * based on the provided start, end, and width - * @param {Number} start - * @param {Number} end - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion - */ - Range.conversion = function (start, end, width, totalHidden) { - if (totalHidden === undefined) { - totalHidden = 0; - } - if (width != 0 && end - start != 0) { - return { - offset: start, - scale: width / (end - start - totalHidden) - }; - } else { - return { - offset: 0, - scale: 1 - }; - } - }; - - /** - * Start dragging horizontally or vertically - * @param {Event} event - * @private - */ - Range.prototype._onDragStart = function (event) { - this.deltaDifference = 0; - this.previousDelta = 0; - - // only allow dragging when configured as movable - if (!this.options.moveable) return; - - // only start dragging when the mouse is inside the current range - if (!this._isInsideRange(event)) return; - - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; - - this.props.touch.start = this.start; - this.props.touch.end = this.end; - this.props.touch.dragging = true; - - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'move'; - } - }; - - /** - * Perform dragging operation - * @param {Event} event - * @private - */ - Range.prototype._onDrag = function (event) { - if (!this.props.touch.dragging) return; - - // only allow dragging when configured as movable - if (!this.options.moveable) return; - - // TODO: this may be redundant in hammerjs2 - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; - - var direction = this.options.direction; - validateDirection(direction); - var delta = direction == 'horizontal' ? event.deltaX : event.deltaY; - delta -= this.deltaDifference; - var interval = this.props.touch.end - this.props.touch.start; - - // normalize dragging speed if cutout is in between. - var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - interval -= duration; - - var width = direction == 'horizontal' ? this.body.domProps.center.width : this.body.domProps.center.height; - var diffRange = -delta / width * interval; - var newStart = this.props.touch.start + diffRange; - var newEnd = this.props.touch.end + diffRange; + var width = direction == 'horizontal' ? this.body.domProps.center.width : this.body.domProps.center.height; + var diffRange = -delta / width * interval; + var newStart = this.props.touch.start + diffRange; + var newEnd = this.props.touch.end + diffRange; // snapping times away from hidden zones var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta - delta, true); @@ -14086,7 +14087,9 @@ return /******/ (function(modules) { // webpackBootstrap /** * Destroy the component. Cleanup DOM and event listeners */ - Component.prototype.destroy = function () {}; + Component.prototype.destroy = function () { + // should be implemented by the component + }; /** * Test whether the component is resized since the last time _isResized() was @@ -14105,8 +14108,6 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Component; - // should be implemented by the component - /***/ }, /* 27 */ /***/ function(module, exports) { @@ -14189,16 +14190,16 @@ return /******/ (function(modules) { // webpackBootstrap } startDate.dayOfYear(start.dayOfYear()); startDate.year(start.year()); - startDate.subtract(7, "days"); + startDate.subtract(7, 'days'); endDate.dayOfYear(start.dayOfYear()); endDate.year(start.year()); - endDate.subtract(7 - offset, "days"); + endDate.subtract(7 - offset, 'days'); - runUntil.add(1, "weeks"); + runUntil.add(1, 'weeks'); break; case "weekly": - var dayOffset = endDate.diff(startDate, "days"); + var dayOffset = endDate.diff(startDate, 'days'); var day = startDate.day(); // set the start date to the range.start @@ -14210,12 +14211,12 @@ return /******/ (function(modules) { // webpackBootstrap // force startDate.day(day); endDate.day(day); - endDate.add(dayOffset, "days"); + endDate.add(dayOffset, 'days'); - startDate.subtract(1, "weeks"); - endDate.subtract(1, "weeks"); + startDate.subtract(1, 'weeks'); + endDate.subtract(1, 'weeks'); - runUntil.add(1, "weeks"); + runUntil.add(1, 'weeks'); break; case "monthly": if (startDate.month() != endDate.month()) { @@ -14223,26 +14224,26 @@ return /******/ (function(modules) { // webpackBootstrap } startDate.month(start.month()); startDate.year(start.year()); - startDate.subtract(1, "months"); + startDate.subtract(1, 'months'); endDate.month(start.month()); endDate.year(start.year()); - endDate.subtract(1, "months"); - endDate.add(offset, "months"); + endDate.subtract(1, 'months'); + endDate.add(offset, 'months'); - runUntil.add(1, "months"); + runUntil.add(1, 'months'); break; case "yearly": if (startDate.year() != endDate.year()) { offset = 1; } startDate.year(start.year()); - startDate.subtract(1, "years"); + startDate.subtract(1, 'years'); endDate.year(start.year()); - endDate.subtract(1, "years"); - endDate.add(offset, "years"); + endDate.subtract(1, 'years'); + endDate.add(offset, 'years'); - runUntil.add(1, "years"); + runUntil.add(1, 'years'); break; default: console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); @@ -14252,20 +14253,20 @@ return /******/ (function(modules) { // webpackBootstrap body.hiddenDates.push({ start: startDate.valueOf(), end: endDate.valueOf() }); switch (hiddenDates[i].repeat) { case "daily": - startDate.add(1, "days"); - endDate.add(1, "days"); + startDate.add(1, 'days'); + endDate.add(1, 'days'); break; case "weekly": - startDate.add(1, "weeks"); - endDate.add(1, "weeks"); + startDate.add(1, 'weeks'); + endDate.add(1, 'weeks'); break; case "monthly": - startDate.add(1, "months"); - endDate.add(1, "months"); + startDate.add(1, 'months'); + endDate.add(1, 'months'); break; case "yearly": - startDate.add(1, "y"); - endDate.add(1, "y"); + startDate.add(1, 'y'); + endDate.add(1, 'y'); break; default: console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); @@ -14312,14 +14313,14 @@ return /******/ (function(modules) { // webpackBootstrap } // j start inside i else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) { - hiddenDates[i].end = hiddenDates[j].end; - hiddenDates[j].remove = true; - } - // j end inside i - else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { - hiddenDates[i].start = hiddenDates[j].start; - hiddenDates[j].remove = true; - } + hiddenDates[i].end = hiddenDates[j].end; + hiddenDates[j].remove = true; + } + // j end inside i + else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { + hiddenDates[i].start = hiddenDates[j].start; + hiddenDates[j].remove = true; + } } } } @@ -15535,7 +15536,7 @@ return /******/ (function(modules) { // webpackBootstrap if (newScrollTop != oldScrollTop) { this._redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already - this.emit('verticalDrag'); + this.emit("verticalDrag"); } }; @@ -17121,25 +17122,25 @@ return /******/ (function(modules) { // webpackBootstrap } // if dragged group was move downwards everything above should have an offset else if (origOrder[curPos + orgOffset] == draggedId) { - orgOffset = 1; - continue; - } - // found a group (apart from dragged group) that has the wrong position -> switch with the - // group at the position where other one should be, fix index arrays and continue - else { - var slippedPosition = newOrder.indexOf(origOrder[curPos + orgOffset]); - var switchGroup = groupsData.get(newOrder[curPos + newOffset]); - var shouldBeGroup = groupsData.get(origOrder[curPos + orgOffset]); - this.options.groupOrderSwap(switchGroup, shouldBeGroup, groupsData); - groupsData.update(switchGroup); - groupsData.update(shouldBeGroup); - - var switchGroupId = newOrder[curPos + newOffset]; - newOrder[curPos + newOffset] = origOrder[curPos + orgOffset]; - newOrder[slippedPosition] = switchGroupId; - - curPos++; - } + orgOffset = 1; + continue; + } + // found a group (apart from dragged group) that has the wrong position -> switch with the + // group at the position where other one should be, fix index arrays and continue + else { + var slippedPosition = newOrder.indexOf(origOrder[curPos + orgOffset]); + var switchGroup = groupsData.get(newOrder[curPos + newOffset]); + var shouldBeGroup = groupsData.get(origOrder[curPos + orgOffset]); + this.options.groupOrderSwap(switchGroup, shouldBeGroup, groupsData); + groupsData.update(switchGroup); + groupsData.update(shouldBeGroup); + + var switchGroupId = newOrder[curPos + newOffset]; + newOrder[curPos + newOffset] = origOrder[curPos + orgOffset]; + newOrder[slippedPosition] = switchGroupId; + + curPos++; + } } } } @@ -17340,16 +17341,16 @@ return /******/ (function(modules) { // webpackBootstrap } } } else { - // add/remove this item from the current selection - var index = selection.indexOf(item.id); - if (index == -1) { - // item is not yet selected -> select it - selection.push(item.id); - } else { - // item is already selected -> deselect it - selection.splice(index, 1); + // add/remove this item from the current selection + var index = selection.indexOf(item.id); + if (index == -1) { + // item is not yet selected -> select it + selection.push(item.id); + } else { + // item is already selected -> deselect it + selection.splice(index, 1); + } } - } this.setSelection(selection); @@ -17615,7 +17616,7 @@ return /******/ (function(modules) { // webpackBootstrap */ TimeStep.prototype.setRange = function (start, end, minimumStep) { if (!(start instanceof Date) || !(end instanceof Date)) { - throw 'No legal start or end date in method setRange'; + throw "No legal start or end date in method setRange"; } this._start = start != undefined ? this.moment(start.valueOf()) : new Date(); @@ -17657,7 +17658,7 @@ return /******/ (function(modules) { // webpackBootstrap this.current.seconds(0); case 'second': this.current.milliseconds(0); - //case 'millisecond': // nothing to do for milliseconds + //case 'millisecond': // nothing to do for milliseconds } if (this.step != 1) { @@ -17950,8 +17951,8 @@ return /******/ (function(modules) { // webpackBootstrap clone.add(1, 'month'); // important: first set Date to 1, after that change the month. } else { - clone.date(1); - } + clone.date(1); + } clone.hours(0); clone.minutes(0); @@ -18238,7 +18239,7 @@ return /******/ (function(modules) { // webpackBootstrap }; this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap. var me = this; - this.itemSet.body.emitter.on('checkRangedItems', function () { + this.itemSet.body.emitter.on("checkRangedItems", function () { me.checkRangedItems = true; }); @@ -19127,10 +19128,10 @@ return /******/ (function(modules) { // webpackBootstrap // a width which will not change when moving the Timeline // So no re-stacking needed, which is nicer for the eye; } else { - this.left = start; - this.width = boxWidth; - contentWidth = Math.min(end - start, this.props.content.width); - } + this.left = start; + this.width = boxWidth; + contentWidth = Math.min(end - start, this.props.content.width); + } this.dom.box.style.left = this.left + 'px'; this.dom.box.style.width = boxWidth + 'px'; @@ -19158,12 +19159,12 @@ return /******/ (function(modules) { // webpackBootstrap contentLeft = -contentWidth; // ensure it's not visible anymore } } else { - if (start < 0) { - contentLeft = -start; - } else { - contentLeft = 0; + if (start < 0) { + contentLeft = -start; + } else { + contentLeft = 0; + } } - } this.dom.content.style.left = contentLeft + 'px'; } }; @@ -19355,17 +19356,23 @@ return /******/ (function(modules) { // webpackBootstrap /** * Repaint the item */ - Item.prototype.redraw = function () {}; + Item.prototype.redraw = function () { + // should be implemented by the item + }; /** * Reposition the Item horizontally */ - Item.prototype.repositionX = function () {}; + Item.prototype.repositionX = function () { + // should be implemented by the item + }; /** * Reposition the Item vertically */ - Item.prototype.repositionY = function () {}; + Item.prototype.repositionY = function () { + // should be implemented by the item + }; /** * Repaint a delete button on the top right of the item when the item is selected @@ -19524,12 +19531,6 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Item; - // should be implemented by the item - - // should be implemented by the item - - // should be implemented by the item - /***/ }, /* 35 */ /***/ function(module, exports, __webpack_require__) { @@ -20223,39 +20224,39 @@ return /******/ (function(modules) { // webpackBootstrap } // and when the orientation is bottom: else { - var newTop = this.parent.top; - var totalHeight = 0; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true) { - var newHeight = subgroups[subgroup].height + margin.item.vertical; - totalHeight += newHeight; - if (subgroups[subgroup].index > subgroupIndex) { - newTop += newHeight; + var newTop = this.parent.top; + var totalHeight = 0; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true) { + var newHeight = subgroups[subgroup].height + margin.item.vertical; + totalHeight += newHeight; + if (subgroups[subgroup].index > subgroupIndex) { + newTop += newHeight; + } } } } + height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; + this.dom.box.style.top = this.parent.height - totalHeight + newTop + 'px'; + this.dom.box.style.bottom = ''; } - height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; - this.dom.box.style.top = this.parent.height - totalHeight + newTop + 'px'; - this.dom.box.style.bottom = ''; - } } // and in the case of no subgroups: else { - // we want backgrounds with groups to only show in groups. - if (this.parent instanceof BackgroundGroup) { - // if the item is not in a group: - height = Math.max(this.parent.height, this.parent.itemSet.body.domProps.center.height, this.parent.itemSet.body.domProps.centerContainer.height); - this.dom.box.style.top = onTop ? '0' : ''; - this.dom.box.style.bottom = onTop ? '' : '0'; - } else { - height = this.parent.height; - // same alignment for items when orientation is top or bottom - this.dom.box.style.top = this.parent.top + 'px'; - this.dom.box.style.bottom = ''; + // we want backgrounds with groups to only show in groups. + if (this.parent instanceof BackgroundGroup) { + // if the item is not in a group: + height = Math.max(this.parent.height, this.parent.itemSet.body.domProps.center.height, this.parent.itemSet.body.domProps.centerContainer.height); + this.dom.box.style.top = onTop ? '0' : ''; + this.dom.box.style.bottom = onTop ? '' : '0'; + } else { + height = this.parent.height; + // same alignment for items when orientation is top or bottom + this.dom.box.style.top = this.parent.top + 'px'; + this.dom.box.style.bottom = ''; + } } - } this.dom.box.style.height = height + 'px'; }; @@ -20886,199 +20887,199 @@ return /******/ (function(modules) { // webpackBootstrap /* 41 */ /***/ function(module, exports, __webpack_require__) { - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; - /** - * Created by Alex on 11/6/2014. - */ - - // https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60 - // if the module has no dependencies, the above pattern can be simplified to - (function (root, factory) { - if (true) { - // AMD. Register as an anonymous module. - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } 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.keycharm = factory(); - } - }(this, function () { - - function keycharm(options) { - var preventDefault = options && options.preventDefault || false; - - var container = options && options.container || window; - - var _exportFunctions = {}; - var _bound = {keydown:{}, keyup:{}}; - var _keys = {}; - var i; - - // a - z - for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} - // A - Z - for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} - // 0 - 9 - for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} - // F1 - F12 - for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} - // num0 - num9 - for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} - - // numpad misc - _keys['num*'] = {code:106, shift: false}; - _keys['num+'] = {code:107, shift: false}; - _keys['num-'] = {code:109, shift: false}; - _keys['num/'] = {code:111, shift: false}; - _keys['num.'] = {code:110, shift: false}; - // arrows - _keys['left'] = {code:37, shift: false}; - _keys['up'] = {code:38, shift: false}; - _keys['right'] = {code:39, shift: false}; - _keys['down'] = {code:40, shift: false}; - // extra keys - _keys['space'] = {code:32, shift: false}; - _keys['enter'] = {code:13, shift: false}; - _keys['shift'] = {code:16, shift: undefined}; - _keys['esc'] = {code:27, shift: false}; - _keys['backspace'] = {code:8, shift: false}; - _keys['tab'] = {code:9, shift: false}; - _keys['ctrl'] = {code:17, shift: false}; - _keys['alt'] = {code:18, shift: false}; - _keys['delete'] = {code:46, shift: false}; - _keys['pageup'] = {code:33, shift: false}; - _keys['pagedown'] = {code:34, shift: false}; - // symbols - _keys['='] = {code:187, shift: false}; - _keys['-'] = {code:189, shift: false}; - _keys[']'] = {code:221, shift: false}; - _keys['['] = {code:219, shift: false}; - - - - var down = function(event) {handleEvent(event,'keydown');}; - var up = function(event) {handleEvent(event,'keyup');}; - - // handle the actualy bound key with the event - var handleEvent = function(event,type) { - if (_bound[type][event.keyCode] !== undefined) { - var bound = _bound[type][event.keyCode]; - for (var i = 0; i < bound.length; i++) { - if (bound[i].shift === undefined) { - bound[i].fn(event); - } - else if (bound[i].shift == true && event.shiftKey == true) { - bound[i].fn(event); - } - else if (bound[i].shift == false && event.shiftKey == false) { - bound[i].fn(event); - } - } - - if (preventDefault == true) { - event.preventDefault(); - } - } - }; - - // bind a key to a callback - _exportFunctions.bind = function(key, callback, type) { - if (type === undefined) { - type = 'keydown'; - } - if (_keys[key] === undefined) { - throw new Error("unsupported key: " + key); - } - if (_bound[type][_keys[key].code] === undefined) { - _bound[type][_keys[key].code] = []; - } - _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); - }; - - - // bind all keys to a call back (demo purposes) - _exportFunctions.bindAll = function(callback, type) { - if (type === undefined) { - type = 'keydown'; - } - for (var key in _keys) { - if (_keys.hasOwnProperty(key)) { - _exportFunctions.bind(key,callback,type); - } - } - }; - - // get the key label from an event - _exportFunctions.getKey = function(event) { - for (var key in _keys) { - if (_keys.hasOwnProperty(key)) { - if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { - return key; - } - else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { - return key; - } - else if (event.keyCode == _keys[key].code && key == 'shift') { - return key; - } - } - } - return "unknown key, currently not supported"; - }; - - // unbind either a specific callback from a key or all of them (by leaving callback undefined) - _exportFunctions.unbind = function(key, callback, type) { - if (type === undefined) { - type = 'keydown'; - } - if (_keys[key] === undefined) { - throw new Error("unsupported key: " + key); - } - if (callback !== undefined) { - var newBindings = []; - var bound = _bound[type][_keys[key].code]; - if (bound !== undefined) { - for (var i = 0; i < bound.length; i++) { - if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { - newBindings.push(_bound[type][_keys[key].code][i]); - } - } - } - _bound[type][_keys[key].code] = newBindings; - } - else { - _bound[type][_keys[key].code] = []; - } - }; - - // reset all bound variables. - _exportFunctions.reset = function() { - _bound = {keydown:{}, keyup:{}}; - }; - - // unbind all listeners and reset all variables. - _exportFunctions.destroy = function() { - _bound = {keydown:{}, keyup:{}}; - container.removeEventListener('keydown', down, true); - container.removeEventListener('keyup', up, true); - }; - - // create listeners. - container.addEventListener('keydown',down,true); - container.addEventListener('keyup',up,true); - - // return the public functions. - return _exportFunctions; - } - - return keycharm; - })); - - + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; + /** + * Created by Alex on 11/6/2014. + */ + + // https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60 + // if the module has no dependencies, the above pattern can be simplified to + (function (root, factory) { + if (true) { + // AMD. Register as an anonymous module. + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } 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.keycharm = factory(); + } + }(this, function () { + + function keycharm(options) { + var preventDefault = options && options.preventDefault || false; + + var container = options && options.container || window; + + var _exportFunctions = {}; + var _bound = {keydown:{}, keyup:{}}; + var _keys = {}; + var i; + + // a - z + for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} + // A - Z + for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} + // 0 - 9 + for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} + // F1 - F12 + for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} + // num0 - num9 + for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} + + // numpad misc + _keys['num*'] = {code:106, shift: false}; + _keys['num+'] = {code:107, shift: false}; + _keys['num-'] = {code:109, shift: false}; + _keys['num/'] = {code:111, shift: false}; + _keys['num.'] = {code:110, shift: false}; + // arrows + _keys['left'] = {code:37, shift: false}; + _keys['up'] = {code:38, shift: false}; + _keys['right'] = {code:39, shift: false}; + _keys['down'] = {code:40, shift: false}; + // extra keys + _keys['space'] = {code:32, shift: false}; + _keys['enter'] = {code:13, shift: false}; + _keys['shift'] = {code:16, shift: undefined}; + _keys['esc'] = {code:27, shift: false}; + _keys['backspace'] = {code:8, shift: false}; + _keys['tab'] = {code:9, shift: false}; + _keys['ctrl'] = {code:17, shift: false}; + _keys['alt'] = {code:18, shift: false}; + _keys['delete'] = {code:46, shift: false}; + _keys['pageup'] = {code:33, shift: false}; + _keys['pagedown'] = {code:34, shift: false}; + // symbols + _keys['='] = {code:187, shift: false}; + _keys['-'] = {code:189, shift: false}; + _keys[']'] = {code:221, shift: false}; + _keys['['] = {code:219, shift: false}; + + + + var down = function(event) {handleEvent(event,'keydown');}; + var up = function(event) {handleEvent(event,'keyup');}; + + // handle the actualy bound key with the event + var handleEvent = function(event,type) { + if (_bound[type][event.keyCode] !== undefined) { + var bound = _bound[type][event.keyCode]; + for (var i = 0; i < bound.length; i++) { + if (bound[i].shift === undefined) { + bound[i].fn(event); + } + else if (bound[i].shift == true && event.shiftKey == true) { + bound[i].fn(event); + } + else if (bound[i].shift == false && event.shiftKey == false) { + bound[i].fn(event); + } + } + + if (preventDefault == true) { + event.preventDefault(); + } + } + }; + + // bind a key to a callback + _exportFunctions.bind = function(key, callback, type) { + if (type === undefined) { + type = 'keydown'; + } + if (_keys[key] === undefined) { + throw new Error("unsupported key: " + key); + } + if (_bound[type][_keys[key].code] === undefined) { + _bound[type][_keys[key].code] = []; + } + _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); + }; + + + // bind all keys to a call back (demo purposes) + _exportFunctions.bindAll = function(callback, type) { + if (type === undefined) { + type = 'keydown'; + } + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + _exportFunctions.bind(key,callback,type); + } + } + }; + + // get the key label from an event + _exportFunctions.getKey = function(event) { + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { + return key; + } + else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { + return key; + } + else if (event.keyCode == _keys[key].code && key == 'shift') { + return key; + } + } + } + return "unknown key, currently not supported"; + }; + + // unbind either a specific callback from a key or all of them (by leaving callback undefined) + _exportFunctions.unbind = function(key, callback, type) { + if (type === undefined) { + type = 'keydown'; + } + if (_keys[key] === undefined) { + throw new Error("unsupported key: " + key); + } + if (callback !== undefined) { + var newBindings = []; + var bound = _bound[type][_keys[key].code]; + if (bound !== undefined) { + for (var i = 0; i < bound.length; i++) { + if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { + newBindings.push(_bound[type][_keys[key].code][i]); + } + } + } + _bound[type][_keys[key].code] = newBindings; + } + else { + _bound[type][_keys[key].code] = []; + } + }; + + // reset all bound variables. + _exportFunctions.reset = function() { + _bound = {keydown:{}, keyup:{}}; + }; + + // unbind all listeners and reset all variables. + _exportFunctions.destroy = function() { + _bound = {keydown:{}, keyup:{}}; + container.removeEventListener('keydown', down, true); + container.removeEventListener('keyup', up, true); + }; + + // create listeners. + container.addEventListener('keydown',down,true); + container.addEventListener('keyup',up,true); + + // return the public functions. + return _exportFunctions; + } + + return keycharm; + })); + + /***/ }, @@ -21551,8 +21552,6 @@ return /******/ (function(modules) { // webpackBootstrap var _ColorPicker2 = _interopRequireDefault(_ColorPicker); - var util = __webpack_require__(1); - /** * The way this works is for all properties of this.possible options, you can supply the property name in any form to list the options. * Boolean options are recognised as Boolean @@ -21567,6 +21566,7 @@ return /******/ (function(modules) { // webpackBootstrap * @param configureOptions | the fully configured and predefined options set found in allOptions.js * @param pixelRatio | canvas pixel ratio */ + var util = __webpack_require__(1); var Configurator = (function () { function Configurator(parentModule, defaultContainer, configureOptions) { @@ -21600,15 +21600,15 @@ return /******/ (function(modules) { // webpackBootstrap this.wrapper = undefined; } + /** + * refresh all options. + * Because all modules parse their options by themselves, we just use their options. We copy them here. + * + * @param options + */ + _createClass(Configurator, [{ key: 'setOptions', - - /** - * refresh all options. - * Because all modules parse their options by themselves, we just use their options. We copy them here. - * - * @param options - */ value: function setOptions(options) { if (options !== undefined) { // reset the popup history because the indices may have been changed. @@ -21660,13 +21660,13 @@ return /******/ (function(modules) { // webpackBootstrap this._create(); } } - }, { - key: '_create', /** * Create all DOM elements * @private */ + }, { + key: '_create', value: function _create() { var _this = this; @@ -21730,13 +21730,13 @@ return /******/ (function(modules) { // webpackBootstrap this._push(); this.colorPicker.insertTo(this.container); } - }, { - key: '_push', /** * draw all DOM elements on the screen * @private */ + }, { + key: '_push', value: function _push() { this.wrapper = document.createElement('div'); this.wrapper.className = 'vis-configuration-wrapper'; @@ -21747,13 +21747,13 @@ return /******/ (function(modules) { // webpackBootstrap this._showPopupIfNeeded(); } - }, { - key: '_clean', /** * delete all DOM elements * @private */ + }, { + key: '_clean', value: function _clean() { for (var i = 0; i < this.domElements.length; i++) { this.wrapper.removeChild(this.domElements[i]); @@ -21767,8 +21767,6 @@ return /******/ (function(modules) { // webpackBootstrap this._removePopup(); } - }, { - key: '_getValue', /** * get the value from the actualOptions if it exists @@ -21776,6 +21774,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ + }, { + key: '_getValue', value: function _getValue(path) { var base = this.moduleOptions; for (var i = 0; i < path.length; i++) { @@ -21788,8 +21788,6 @@ return /******/ (function(modules) { // webpackBootstrap } return base; } - }, { - key: '_makeItem', /** * all option elements are wrapped in an item @@ -21797,6 +21795,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param domElements * @private */ + }, { + key: '_makeItem', value: function _makeItem(path) { var _arguments = arguments, _this2 = this; @@ -21825,22 +21825,20 @@ return /******/ (function(modules) { // webpackBootstrap } return 0; } - }, { - key: '_makeHeader', /** * header for major subjects * @param name * @private */ + }, { + key: '_makeHeader', value: function _makeHeader(name) { var div = document.createElement('div'); div.className = 'vis-configuration vis-config-header'; div.innerHTML = name; this._makeItem([], div); } - }, { - key: '_makeLabel', /** * make a label, if it is an object label, it gets different styling. @@ -21850,6 +21848,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {HTMLElement} * @private */ + }, { + key: '_makeLabel', value: function _makeLabel(name, path) { var objectLabel = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; @@ -21862,8 +21862,6 @@ return /******/ (function(modules) { // webpackBootstrap } return div; } - }, { - key: '_makeDropdown', /** * make a dropdown list for multiple possible string optoins @@ -21872,6 +21870,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ + }, { + key: '_makeDropdown', value: function _makeDropdown(arr, value, path) { var select = document.createElement('select'); select.className = 'vis-configuration vis-config-select'; @@ -21900,8 +21900,6 @@ return /******/ (function(modules) { // webpackBootstrap var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, select); } - }, { - key: '_makeRange', /** * make a range object for numeric options @@ -21910,6 +21908,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ + }, { + key: '_makeRange', value: function _makeRange(arr, value, path) { var defaultValue = arr[0]; var min = arr[1]; @@ -21970,8 +21970,6 @@ return /******/ (function(modules) { // webpackBootstrap this._setupPopup(popupString, itemIndex); } } - }, { - key: '_setupPopup', /** * prepare the popup @@ -21979,13 +21977,15 @@ return /******/ (function(modules) { // webpackBootstrap * @param index * @private */ + }, { + key: '_setupPopup', value: function _setupPopup(string, index) { var _this3 = this; if (this.initialized === true && this.allowCreation === true && this.popupCounter < this.popupLimit) { - var div = document.createElement('div'); - div.id = 'vis-configuration-popup'; - div.className = 'vis-configuration-popup'; + var div = document.createElement("div"); + div.id = "vis-configuration-popup"; + div.className = "vis-configuration-popup"; div.innerHTML = string; div.onclick = function () { _this3._removePopup(); @@ -21994,13 +21994,13 @@ return /******/ (function(modules) { // webpackBootstrap this.popupDiv = { html: div, index: index }; } } - }, { - key: '_removePopup', /** * remove the popup from the dom * @private */ + }, { + key: '_removePopup', value: function _removePopup() { if (this.popupDiv.html !== undefined) { this.popupDiv.html.parentNode.removeChild(this.popupDiv.html); @@ -22009,21 +22009,21 @@ return /******/ (function(modules) { // webpackBootstrap this.popupDiv = {}; } } - }, { - key: '_showPopupIfNeeded', /** * Show the popup if it is needed. * @private */ + }, { + key: '_showPopupIfNeeded', value: function _showPopupIfNeeded() { var _this4 = this; if (this.popupDiv.html !== undefined) { var correspondingElement = this.domElements[this.popupDiv.index]; var rect = correspondingElement.getBoundingClientRect(); - this.popupDiv.html.style.left = rect.left + 'px'; - this.popupDiv.html.style.top = rect.top - 30 + 'px'; // 30 is the height; + this.popupDiv.html.style.left = rect.left + "px"; + this.popupDiv.html.style.top = rect.top - 30 + "px"; // 30 is the height; document.body.appendChild(this.popupDiv.html); this.popupDiv.hideTimeout = setTimeout(function () { _this4.popupDiv.html.style.opacity = 0; @@ -22033,8 +22033,6 @@ return /******/ (function(modules) { // webpackBootstrap }, 1800); } } - }, { - key: '_makeCheckbox', /** * make a checkbox for boolean options. @@ -22043,6 +22041,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ + }, { + key: '_makeCheckbox', value: function _makeCheckbox(defaultValue, value, path) { var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; @@ -22069,8 +22069,6 @@ return /******/ (function(modules) { // webpackBootstrap var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, checkbox); } - }, { - key: '_makeTextInput', /** * make a text input field for string options. @@ -22079,6 +22077,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ + }, { + key: '_makeTextInput', value: function _makeTextInput(defaultValue, value, path) { var checkbox = document.createElement('input'); checkbox.type = 'text'; @@ -22096,8 +22096,6 @@ return /******/ (function(modules) { // webpackBootstrap var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, checkbox); } - }, { - key: '_makeColorField', /** * make a color field with a color picker for color fields @@ -22106,6 +22104,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ + }, { + key: '_makeColorField', value: function _makeColorField(arr, value, path) { var _this5 = this; @@ -22128,8 +22128,6 @@ return /******/ (function(modules) { // webpackBootstrap var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, div); } - }, { - key: '_showColorPicker', /** * used by the color buttons to call the color picker. @@ -22139,6 +22137,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ + }, { + key: '_showColorPicker', value: function _showColorPicker(value, div, path) { var _this6 = this; @@ -22154,8 +22154,6 @@ return /******/ (function(modules) { // webpackBootstrap _this6._update(colorString, path); }); } - }, { - key: '_handleObject', /** * parse an object and draw the correct items @@ -22163,6 +22161,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ + }, { + key: '_handleObject', value: function _handleObject(obj) { var path = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; var checkOnly = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; @@ -22233,8 +22233,6 @@ return /******/ (function(modules) { // webpackBootstrap } return visibleInSet; } - }, { - key: '_handleArray', /** * handle the array type of option @@ -22244,6 +22242,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ + }, { + key: '_handleArray', value: function _handleArray(arr, value, path) { if (typeof arr[0] === 'string' && arr[0] === 'color') { this._makeColorField(arr, value, path); @@ -22262,8 +22262,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_update', /** * called to update the network with the new settings. @@ -22271,11 +22269,13 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ + }, { + key: '_update', value: function _update(value, path) { var options = this._constructOptions(value, path); if (this.parent.body && this.parent.body.emitter && this.parent.body.emitter.emit) { - this.parent.body.emitter.emit('configChange', options); + this.parent.body.emitter.emit("configChange", options); } this.initialized = true; this.parent.setOptions(options); @@ -22369,13 +22369,13 @@ return /******/ (function(modules) { // webpackBootstrap this._create(); } + /** + * this inserts the colorPicker into a div from the DOM + * @param container + */ + _createClass(ColorPicker, [{ key: 'insertTo', - - /** - * this inserts the colorPicker into a div from the DOM - * @param container - */ value: function insertTo(container) { if (this.hammer !== undefined) { this.hammer.destroy(); @@ -22387,18 +22387,18 @@ return /******/ (function(modules) { // webpackBootstrap this._setSize(); } - }, { - key: 'setCallback', /** * the callback is executed on apply and save. Bind it to the application * @param callback */ + }, { + key: 'setCallback', value: function setCallback(callback) { if (typeof callback === 'function') { this.updateCallback = callback; } else { - throw new Error('Function attempted to set as colorPicker callback is not a function.'); + throw new Error("Function attempted to set as colorPicker callback is not a function."); } } }, { @@ -22409,8 +22409,6 @@ return /******/ (function(modules) { // webpackBootstrap return htmlColors[color]; } } - }, { - key: 'setColor', /** * Set the color of the colorPicker @@ -22424,6 +22422,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param color * @param setInitial */ + }, { + key: 'setColor', value: function setColor(color) { var setInitial = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; @@ -22462,19 +22462,19 @@ return /******/ (function(modules) { // webpackBootstrap // set color if (rgba === undefined) { - throw new Error('Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: ' + JSON.stringify(color)); + throw new Error("Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: " + JSON.stringify(color)); } else { this._setColor(rgba, setInitial); } } - }, { - key: 'show', /** * this shows the color picker at a location. The hue circle is constructed once and stored. * @param x * @param y */ + }, { + key: 'show', value: function show(x, y) { this.applied = false; this.frame.style.display = 'block'; @@ -22482,8 +22482,6 @@ return /******/ (function(modules) { // webpackBootstrap this.frame.style.left = x + 'px'; this._generateHueCircle(); } - }, { - key: '_hide', // ------------------------------------------ PRIVATE ----------------------------- // @@ -22493,6 +22491,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param storePrevious * @private */ + }, { + key: '_hide', value: function _hide() { var storePrevious = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0]; @@ -22507,46 +22507,44 @@ return /******/ (function(modules) { // webpackBootstrap this.frame.style.display = 'none'; } - }, { - key: '_save', /** * bound to the save button. Saves and hides. * @private */ + }, { + key: '_save', value: function _save() { this.updateCallback(this.color); this.applied = false; this._hide(); } - }, { - key: '_apply', /** * Bound to apply button. Saves but does not close. Is undone by the cancel button. * @private */ + }, { + key: '_apply', value: function _apply() { this.applied = true; this.updateCallback(this.color); this._updatePicker(this.color); } - }, { - key: '_loadLast', /** * load the color from the previous session. * @private */ + }, { + key: '_loadLast', value: function _loadLast() { if (this.previousColor !== undefined) { this.setColor(this.previousColor, false); } else { - alert('There is no last color to load...'); + alert("There is no last color to load..."); } } - }, { - key: '_setColor', /** * set the color, place the picker @@ -22554,6 +22552,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param setInitial * @private */ + }, { + key: '_setColor', value: function _setColor(rgba) { var setInitial = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; @@ -22575,26 +22575,26 @@ return /******/ (function(modules) { // webpackBootstrap this._updatePicker(rgba); } - }, { - key: '_setOpacity', /** * bound to opacity control * @param value * @private */ + }, { + key: '_setOpacity', value: function _setOpacity(value) { this.color.a = value / 100; this._updatePicker(this.color); } - }, { - key: '_setBrightness', /** * bound to brightness control * @param value * @private */ + }, { + key: '_setBrightness', value: function _setBrightness(value) { var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); hsv.v = value / 100; @@ -22603,14 +22603,14 @@ return /******/ (function(modules) { // webpackBootstrap this.color = rgba; this._updatePicker(); } - }, { - key: '_updatePicker', /** * update the colorpicker. A black circle overlays the hue circle to mimic the brightness decreasing. * @param rgba * @private */ + }, { + key: '_updatePicker', value: function _updatePicker() { var rgba = arguments.length <= 0 || arguments[0] === undefined ? this.color : arguments[0]; @@ -22637,13 +22637,13 @@ return /******/ (function(modules) { // webpackBootstrap this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; } - }, { - key: '_setSize', /** * used by create to set the size of the canvas. * @private */ + }, { + key: '_setSize', value: function _setSize() { this.colorPickerCanvas.style.width = '100%'; this.colorPickerCanvas.style.height = '100%'; @@ -22651,14 +22651,14 @@ return /******/ (function(modules) { // webpackBootstrap this.colorPickerCanvas.width = 289 * this.pixelRatio; this.colorPickerCanvas.height = 289 * this.pixelRatio; } - }, { - key: '_create', /** * create all dom elements * TODO: cleanup, lots of similar dom elements * @private */ + }, { + key: '_create', value: function _create() { this.frame = document.createElement('div'); this.frame.className = 'vis-color-picker'; @@ -22679,10 +22679,10 @@ return /******/ (function(modules) { // webpackBootstrap noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; this.colorPickerCanvas.appendChild(noCanvas); } else { - var ctx = this.colorPickerCanvas.getContext('2d'); + var ctx = this.colorPickerCanvas.getContext("2d"); this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); - this.colorPickerCanvas.getContext('2d').setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + this.colorPickerCanvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); } this.colorPickerDiv.className = 'vis-color'; @@ -22731,39 +22731,39 @@ return /******/ (function(modules) { // webpackBootstrap me._setBrightness(this.value); }; - this.brightnessLabel = document.createElement('div'); - this.brightnessLabel.className = 'vis-label vis-brightness'; + this.brightnessLabel = document.createElement("div"); + this.brightnessLabel.className = "vis-label vis-brightness"; this.brightnessLabel.innerHTML = 'brightness:'; - this.opacityLabel = document.createElement('div'); - this.opacityLabel.className = 'vis-label vis-opacity'; + this.opacityLabel = document.createElement("div"); + this.opacityLabel.className = "vis-label vis-opacity"; this.opacityLabel.innerHTML = 'opacity:'; - this.newColorDiv = document.createElement('div'); - this.newColorDiv.className = 'vis-new-color'; + this.newColorDiv = document.createElement("div"); + this.newColorDiv.className = "vis-new-color"; this.newColorDiv.innerHTML = 'new'; - this.initialColorDiv = document.createElement('div'); - this.initialColorDiv.className = 'vis-initial-color'; + this.initialColorDiv = document.createElement("div"); + this.initialColorDiv.className = "vis-initial-color"; this.initialColorDiv.innerHTML = 'initial'; - this.cancelButton = document.createElement('div'); - this.cancelButton.className = 'vis-button vis-cancel'; + this.cancelButton = document.createElement("div"); + this.cancelButton.className = "vis-button vis-cancel"; this.cancelButton.innerHTML = 'cancel'; this.cancelButton.onclick = this._hide.bind(this, false); - this.applyButton = document.createElement('div'); - this.applyButton.className = 'vis-button vis-apply'; + this.applyButton = document.createElement("div"); + this.applyButton.className = "vis-button vis-apply"; this.applyButton.innerHTML = 'apply'; this.applyButton.onclick = this._apply.bind(this); - this.saveButton = document.createElement('div'); - this.saveButton.className = 'vis-button vis-save'; + this.saveButton = document.createElement("div"); + this.saveButton.className = "vis-button vis-save"; this.saveButton.innerHTML = 'save'; this.saveButton.onclick = this._save.bind(this); - this.loadButton = document.createElement('div'); - this.loadButton.className = 'vis-button vis-load'; + this.loadButton = document.createElement("div"); + this.loadButton.className = "vis-button vis-load"; this.loadButton.innerHTML = 'load last'; this.loadButton.onclick = this._loadLast.bind(this); @@ -22781,13 +22781,13 @@ return /******/ (function(modules) { // webpackBootstrap this.frame.appendChild(this.saveButton); this.frame.appendChild(this.loadButton); } - }, { - key: '_bindHammer', /** * bind hammer to the color picker * @private */ + }, { + key: '_bindHammer', value: function _bindHammer() { var _this = this; @@ -22812,13 +22812,13 @@ return /******/ (function(modules) { // webpackBootstrap _this._moveSelector(event); }); } - }, { - key: '_generateHueCircle', /** * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown. * @private */ + }, { + key: '_generateHueCircle', value: function _generateHueCircle() { if (this.generated === false) { var ctx = this.colorPickerCanvas.getContext('2d'); @@ -22860,8 +22860,6 @@ return /******/ (function(modules) { // webpackBootstrap } this.generated = true; } - }, { - key: '_moveSelector', /** * move the selector. This is called by hammer functions. @@ -22869,6 +22867,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param event * @private */ + }, { + key: '_moveSelector', value: function _moveSelector(event) { var rect = this.colorPickerDiv.getBoundingClientRect(); var left = event.center.x - rect.left; @@ -22940,15 +22940,15 @@ return /******/ (function(modules) { // webpackBootstrap _classCallCheck(this, Validator); } + /** + * Main function to be called + * @param options + * @param subObject + * @returns {boolean} + */ + _createClass(Validator, null, [{ key: 'validate', - - /** - * Main function to be called - * @param options - * @param subObject - * @returns {boolean} - */ value: function validate(options, referenceOptions, subObject) { errorFound = false; allOptions = referenceOptions; @@ -22959,8 +22959,6 @@ return /******/ (function(modules) { // webpackBootstrap Validator.parse(options, usedOptions, []); return errorFound; } - }, { - key: 'parse', /** * Will traverse an object recursively and check every value @@ -22968,6 +22966,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param referenceOptions * @param path */ + }, { + key: 'parse', value: function parse(options, referenceOptions, path) { for (var option in options) { if (options.hasOwnProperty(option)) { @@ -22975,8 +22975,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'check', /** * Check every value. If the value is an object, call the parse function on that object. @@ -22985,6 +22983,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param referenceOptions * @param path */ + }, { + key: 'check', value: function check(option, options, referenceOptions, path) { if (referenceOptions[option] === undefined && referenceOptions.__any__ === undefined) { Validator.getSuggestion(option, referenceOptions, path); @@ -23006,8 +23006,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'checkFields', /** * @@ -23018,6 +23016,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {String} refOptionType | This is the type object from the reference options * @param {Array} path | where in the object is the option */ + }, { + key: 'checkFields', value: function checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path) { var optionType = Validator.getType(options[option]); var refOptionType = refOptionObj[optionType]; @@ -23027,11 +23027,11 @@ return /******/ (function(modules) { // webpackBootstrap if (refOptionType.indexOf(options[option]) === -1) { console.log('%cInvalid option detected in "' + option + '".' + ' Allowed values are:' + Validator.print(refOptionType) + ' not "' + options[option] + '". ' + Validator.printLocation(path, option), printStyle); errorFound = true; - } else if (optionType === 'object' && referenceOption !== '__any__') { + } else if (optionType === 'object' && referenceOption !== "__any__") { path = util.copyAndExtendArray(path, option); Validator.parse(options[option], referenceOptions[referenceOption], path); } - } else if (optionType === 'object' && referenceOption !== '__any__') { + } else if (optionType === 'object' && referenceOption !== "__any__") { path = util.copyAndExtendArray(path, option); Validator.parse(options[option], referenceOptions[referenceOption], path); } @@ -23104,8 +23104,6 @@ return /******/ (function(modules) { // webpackBootstrap errorFound = true; } - }, { - key: 'findInOptions', /** * traverse the options in search for a match. @@ -23115,6 +23113,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param recursive * @returns {{closestMatch: string, path: Array, distance: number}} */ + }, { + key: 'findInOptions', value: function findInOptions(option, options, path) { var recursive = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3]; @@ -23174,10 +23174,8 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: 'print', value: function print(options) { - return JSON.stringify(options).replace(/(\")|(\[)|(\])|(,"__type__")/g, '').replace(/(\,)/g, ', '); + return JSON.stringify(options).replace(/(\")|(\[)|(\])|(,"__type__")/g, "").replace(/(\,)/g, ', '); } - }, { - key: 'levenshteinDistance', // Compute the edit distance between the two given strings // http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript @@ -23187,6 +23185,8 @@ return /******/ (function(modules) { // webpackBootstrap 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. */ + }, { + key: 'levenshteinDistance', value: function levenshteinDistance(a, b) { if (a.length === 0) return b.length; if (b.length === 0) return a.length; @@ -23685,7 +23685,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this.linegraph.groups[groupId] !== undefined) { return this.linegraph.groups[groupId].getLegend(width, height); } else { - return 'cannot find group:' + groupId; + return "cannot find group:" + groupId; } }; @@ -24002,7 +24002,7 @@ return /******/ (function(modules) { // webpackBootstrap this.updateSVGheight = true; this.updateSVGheightOnResize = true; } else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { - if (parseInt((options.graphHeight + '').replace('px', '')) < this.body.domProps.centerContainer.height) { + if (parseInt((options.graphHeight + '').replace("px", '')) < this.body.domProps.centerContainer.height) { this.updateSVGheight = true; } } @@ -24356,7 +24356,7 @@ return /******/ (function(modules) { // webpackBootstrap this.svg.style.left = util.option.asSize(-this.props.width); // if the height of the graph is set as proportional, change the height of the svg - if ((this.options.height + '').indexOf('%') != -1 || this.updateSVGheightOnResize == true) { + if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) { this.updateSVGheight = true; } } @@ -24448,7 +24448,7 @@ return /******/ (function(modules) { // webpackBootstrap return true; } else { if (this.COUNTER > MAX_CYCLES) { - console.log('WARNING: there may be an infinite loop in the _updateGraph emitter cycle.'); + console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle."); } this.COUNTER = 0; this.abortedGraphUpdate = false; @@ -24847,7 +24847,7 @@ return /******/ (function(modules) { // webpackBootstrap this.conversionFactor = 1; this.setOptions(options); - this.width = Number(('' + this.options.width).replace('px', '')); + this.width = Number(('' + this.options.width).replace("px", "")); this.minWidth = this.width; this.height = this.linegraphSVG.offsetHeight; this.hidden = false; @@ -24868,7 +24868,7 @@ return /******/ (function(modules) { // webpackBootstrap this._create(); var me = this; - this.body.emitter.on('verticalDrag', function () { + this.body.emitter.on("verticalDrag", function () { me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; }); } @@ -24902,7 +24902,7 @@ return /******/ (function(modules) { // webpackBootstrap var fields = ['orientation', 'showMinorLabels', 'showMajorLabels', 'icons', 'majorLinesOffset', 'minorLinesOffset', 'labelOffsetX', 'labelOffsetY', 'iconWidth', 'width', 'visible', 'left', 'right', 'alignZeros']; util.selectiveExtend(fields, this.options, options); - this.minWidth = Number(('' + this.options.width).replace('px', '')); + this.minWidth = Number(('' + this.options.width).replace("px", "")); if (redraw === true && this.dom.frame) { this.hide(); @@ -24925,12 +24925,12 @@ return /******/ (function(modules) { // webpackBootstrap this.dom.lineContainer.style.position = 'relative'; // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - this.svg.style.position = 'absolute'; + this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); + this.svg.style.position = "absolute"; this.svg.style.top = '0px'; this.svg.style.height = '100%'; this.svg.style.width = '100%'; - this.svg.style.display = 'block'; + this.svg.style.display = "block"; this.dom.frame.appendChild(this.svg); }; @@ -25044,11 +25044,11 @@ return /******/ (function(modules) { // webpackBootstrap this.hide(); } else { this.show(); - this.height = Number(this.linegraphSVG.style.height.replace('px', '')); + this.height = Number(this.linegraphSVG.style.height.replace("px", "")); // svg offsetheight did not work in firefox and explorer... this.dom.lineContainer.style.height = this.height + 'px'; - this.width = this.options.visible === true ? Number(('' + this.options.width).replace('px', '')) : 0; + this.width = this.options.visible === true ? Number(('' + this.options.width).replace("px", "")) : 0; var props = this.props; var frame = this.dom.frame; @@ -25078,7 +25078,7 @@ return /******/ (function(modules) { // webpackBootstrap frame.style.left = '0'; frame.style.bottom = ''; frame.style.width = this.width + 'px'; - frame.style.height = this.height + 'px'; + frame.style.height = this.height + "px"; this.props.width = this.body.domProps.left.width; this.props.height = this.body.domProps.left.height; } else { @@ -25087,7 +25087,7 @@ return /******/ (function(modules) { // webpackBootstrap frame.style.bottom = '0'; frame.style.left = '0'; frame.style.width = this.width + 'px'; - frame.style.height = this.height + 'px'; + frame.style.height = this.height + "px"; this.props.width = this.body.domProps.right.width; this.props.height = this.body.domProps.right.height; } @@ -25211,7 +25211,7 @@ return /******/ (function(modules) { // webpackBootstrap // this will resize the yAxis to accommodate the labels. if (this.maxLabelSize > this.width - offset && this.options.visible === true) { this.width = this.maxLabelSize + offset; - this.options.width = this.width + 'px'; + this.options.width = this.width + "px"; DOMutil.cleanupElements(this.DOMelements.lines); DOMutil.cleanupElements(this.DOMelements.labels); this.redraw(); @@ -25219,17 +25219,17 @@ return /******/ (function(modules) { // webpackBootstrap } // this will resize the yAxis if it is too big for the labels. else if (this.maxLabelSize < this.width - offset && this.options.visible === true && this.width > this.minWidth) { - this.width = Math.max(this.minWidth, this.maxLabelSize + offset); - this.options.width = this.width + 'px'; - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - this.redraw(); - resized = true; - } else { - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - resized = false; - } + this.width = Math.max(this.minWidth, this.maxLabelSize + offset); + this.options.width = this.width + "px"; + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + this.redraw(); + resized = true; + } else { + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + resized = false; + } return resized; }; @@ -25260,10 +25260,10 @@ return /******/ (function(modules) { // webpackBootstrap label.innerHTML = text; if (orientation === 'left') { label.style.left = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = 'right'; + label.style.textAlign = "right"; } else { label.style.right = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = 'left'; + label.style.textAlign = "left"; } label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; @@ -25747,29 +25747,29 @@ return /******/ (function(modules) { // webpackBootstrap var fillHeight = iconHeight * 0.5; var path, fillPath; - var outline = DOMutil.getSVGElement('rect', JSONcontainer, SVGcontainer); - outline.setAttributeNS(null, 'x', x); - outline.setAttributeNS(null, 'y', y - fillHeight); - outline.setAttributeNS(null, 'width', iconWidth); - outline.setAttributeNS(null, 'height', 2 * fillHeight); - outline.setAttributeNS(null, 'class', 'vis-outline'); + var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); + outline.setAttributeNS(null, "x", x); + outline.setAttributeNS(null, "y", y - fillHeight); + outline.setAttributeNS(null, "width", iconWidth); + outline.setAttributeNS(null, "height", 2 * fillHeight); + outline.setAttributeNS(null, "class", "vis-outline"); if (this.options.style == 'line') { - path = DOMutil.getSVGElement('path', JSONcontainer, SVGcontainer); - path.setAttributeNS(null, 'class', this.className); + path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + path.setAttributeNS(null, "class", this.className); if (this.style !== undefined) { - path.setAttributeNS(null, 'style', this.style); + path.setAttributeNS(null, "style", this.style); } - path.setAttributeNS(null, 'd', 'M' + x + ',' + y + ' L' + (x + iconWidth) + ',' + y + ''); + path.setAttributeNS(null, "d", "M" + x + "," + y + " L" + (x + iconWidth) + "," + y + ""); if (this.options.shaded.enabled == true) { - fillPath = DOMutil.getSVGElement('path', JSONcontainer, SVGcontainer); + fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); if (this.options.shaded.orientation == 'top') { - fillPath.setAttributeNS(null, 'd', 'M' + x + ', ' + (y - fillHeight) + 'L' + x + ',' + y + ' L' + (x + iconWidth) + ',' + y + ' L' + (x + iconWidth) + ',' + (y - fillHeight)); + fillPath.setAttributeNS(null, "d", "M" + x + ", " + (y - fillHeight) + "L" + x + "," + y + " L" + (x + iconWidth) + "," + y + " L" + (x + iconWidth) + "," + (y - fillHeight)); } else { - fillPath.setAttributeNS(null, 'd', 'M' + x + ',' + y + ' ' + 'L' + x + ',' + (y + fillHeight) + ' ' + 'L' + (x + iconWidth) + ',' + (y + fillHeight) + 'L' + (x + iconWidth) + ',' + y); + fillPath.setAttributeNS(null, "d", "M" + x + "," + y + " " + "L" + x + "," + (y + fillHeight) + " " + "L" + (x + iconWidth) + "," + (y + fillHeight) + "L" + (x + iconWidth) + "," + y); } - fillPath.setAttributeNS(null, 'class', this.className + ' vis-icon-fill'); + fillPath.setAttributeNS(null, "class", this.className + " vis-icon-fill"); } if (this.options.drawPoints.enabled == true) { @@ -25801,7 +25801,7 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOrientation|*)}} */ GraphGroup.prototype.getLegend = function (iconWidth, iconHeight) { - var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + var svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); this.drawIcon(0, 0.5 * iconHeight, [], svg, iconWidth, iconHeight); return { icon: svg, label: this.content, orientation: this.options.yAxisOrientation }; }; @@ -25941,9 +25941,9 @@ return /******/ (function(modules) { // webpackBootstrap var path, d; var svgHeight = Number(framework.svg.style.height.replace('px', '')); path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - path.setAttributeNS(null, 'class', group.className); + path.setAttributeNS(null, "class", group.className); if (group.style !== undefined) { - path.setAttributeNS(null, 'style', group.style); + path.setAttributeNS(null, "style", group.style); } // construct path from dataset @@ -26523,16 +26523,16 @@ return /******/ (function(modules) { // webpackBootstrap Legend.prototype._create = function () { this.dom.frame = document.createElement('div'); this.dom.frame.className = 'vis-legend'; - this.dom.frame.style.position = 'absolute'; - this.dom.frame.style.top = '10px'; - this.dom.frame.style.display = 'block'; + this.dom.frame.style.position = "absolute"; + this.dom.frame.style.top = "10px"; + this.dom.frame.style.display = "block"; this.dom.textArea = document.createElement('div'); this.dom.textArea.className = 'vis-legend-text'; - this.dom.textArea.style.position = 'relative'; - this.dom.textArea.style.top = '0px'; + this.dom.textArea.style.position = "relative"; + this.dom.textArea.style.top = "0px"; - this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); this.svg.style.position = 'absolute'; this.svg.style.top = 0 + 'px'; this.svg.style.width = this.options.iconSize + 5 + 'px'; @@ -26588,16 +26588,16 @@ return /******/ (function(modules) { // webpackBootstrap this.show(); if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { this.dom.frame.style.left = '4px'; - this.dom.frame.style.textAlign = 'left'; - this.dom.textArea.style.textAlign = 'left'; + this.dom.frame.style.textAlign = "left"; + this.dom.textArea.style.textAlign = "left"; this.dom.textArea.style.left = this.options.iconSize + 15 + 'px'; this.dom.textArea.style.right = ''; this.svg.style.left = 0 + 'px'; this.svg.style.right = ''; } else { this.dom.frame.style.right = '4px'; - this.dom.frame.style.textAlign = 'right'; - this.dom.textArea.style.textAlign = 'right'; + this.dom.frame.style.textAlign = "right"; + this.dom.textArea.style.textAlign = "right"; this.dom.textArea.style.right = this.options.iconSize + 15 + 'px'; this.dom.textArea.style.left = ''; this.svg.style.right = 0 + 'px'; @@ -26605,11 +26605,11 @@ return /******/ (function(modules) { // webpackBootstrap } if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { - this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace('px', '')) + 'px'; + this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px", "")) + 'px'; this.dom.frame.style.bottom = ''; } else { var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; - this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace('px', '')) + 'px'; + this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px", "")) + 'px'; this.dom.frame.style.top = ''; } @@ -26970,35 +26970,35 @@ return /******/ (function(modules) { // webpackBootstrap var _modulesPhysicsEngine2 = _interopRequireDefault(_modulesPhysicsEngine); - var _modulesClustering = __webpack_require__(99); + var _modulesClustering = __webpack_require__(100); var _modulesClustering2 = _interopRequireDefault(_modulesClustering); - var _modulesCanvasRenderer = __webpack_require__(101); + var _modulesCanvasRenderer = __webpack_require__(102); var _modulesCanvasRenderer2 = _interopRequireDefault(_modulesCanvasRenderer); - var _modulesCanvas = __webpack_require__(102); + var _modulesCanvas = __webpack_require__(103); var _modulesCanvas2 = _interopRequireDefault(_modulesCanvas); - var _modulesView = __webpack_require__(103); + var _modulesView = __webpack_require__(104); var _modulesView2 = _interopRequireDefault(_modulesView); - var _modulesInteractionHandler = __webpack_require__(105); + var _modulesInteractionHandler = __webpack_require__(106); var _modulesInteractionHandler2 = _interopRequireDefault(_modulesInteractionHandler); - var _modulesSelectionHandler = __webpack_require__(108); + var _modulesSelectionHandler = __webpack_require__(109); var _modulesSelectionHandler2 = _interopRequireDefault(_modulesSelectionHandler); - var _modulesLayoutEngine = __webpack_require__(109); + var _modulesLayoutEngine = __webpack_require__(110); var _modulesLayoutEngine2 = _interopRequireDefault(_modulesLayoutEngine); - var _modulesManipulationSystem = __webpack_require__(110); + var _modulesManipulationSystem = __webpack_require__(111); var _modulesManipulationSystem2 = _interopRequireDefault(_modulesManipulationSystem); @@ -27010,25 +27010,12 @@ return /******/ (function(modules) { // webpackBootstrap var _sharedValidator2 = _interopRequireDefault(_sharedValidator); - var _optionsJs = __webpack_require__(111); + var _optionsJs = __webpack_require__(112); - var _modulesKamadaKawaiJs = __webpack_require__(112); + var _modulesKamadaKawaiJs = __webpack_require__(113); var _modulesKamadaKawaiJs2 = _interopRequireDefault(_modulesKamadaKawaiJs); - __webpack_require__(114); - - var Emitter = __webpack_require__(12); - var Hammer = __webpack_require__(20); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(8); - var DataView = __webpack_require__(10); - var dotparser = __webpack_require__(115); - var gephiParser = __webpack_require__(116); - var Images = __webpack_require__(117); - var Activator = __webpack_require__(40); - var locales = __webpack_require__(118); - /** * @constructor Network * Create a network visualization, displaying nodes and edges. @@ -27040,6 +27027,19 @@ return /******/ (function(modules) { // webpackBootstrap * {Array} edges * @param {Object} options Options */ + __webpack_require__(115); + + var Emitter = __webpack_require__(12); + var Hammer = __webpack_require__(20); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(8); + var DataView = __webpack_require__(10); + var dotparser = __webpack_require__(116); + var gephiParser = __webpack_require__(117); + var Images = __webpack_require__(118); + var Activator = __webpack_require__(40); + var locales = __webpack_require__(119); + function Network(container, data, options) { var _this = this; @@ -27104,7 +27104,7 @@ return /******/ (function(modules) { // webpackBootstrap // setting up all modules this.images = new Images(function () { - return _this.body.emitter.emit('_requestRedraw'); + return _this.body.emitter.emit("_requestRedraw"); }); // object with images this.groups = new _modulesGroups2['default'](); // object with groups this.canvas = new _modulesCanvas2['default'](this.body); // DOM handler @@ -27120,8 +27120,8 @@ return /******/ (function(modules) { // webpackBootstrap this.nodesHandler = new _modulesNodesHandler2['default'](this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options this.edgesHandler = new _modulesEdgesHandler2['default'](this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options - this.body.modules['kamadaKawai'] = new _modulesKamadaKawaiJs2['default'](this.body, 150, 0.05); // Layouting algorithm. - this.body.modules['clustering'] = this.clustering; + this.body.modules["kamadaKawai"] = new _modulesKamadaKawaiJs2['default'](this.body, 150, 0.05); // Layouting algorithm. + this.body.modules["clustering"] = this.clustering; // create the DOM elements this.canvas._create(); @@ -27172,7 +27172,7 @@ return /******/ (function(modules) { // webpackBootstrap // reload the settings of the nodes to apply changes in groups that are not referenced by pointer. if (options.groups !== undefined) { - this.body.emitter.emit('refreshNodes'); + this.body.emitter.emit("refreshNodes"); } // these two do not have options at the moment, here for completeness //this.view.setOptions(options.view); @@ -27213,7 +27213,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this.activator === undefined) { this.activator = new Activator(this.canvas.frame); this.activator.on('change', function () { - _this2.body.emitter.emit('activate'); + _this2.body.emitter.emit("activate"); }); } } else { @@ -27221,15 +27221,15 @@ return /******/ (function(modules) { // webpackBootstrap this.activator.destroy(); delete this.activator; } - this.body.emitter.emit('activate'); + this.body.emitter.emit("activate"); } } else { - this.body.emitter.emit('activate'); + this.body.emitter.emit("activate"); } this.canvas.setSize(); // start the physics simulation. Can be safely called multiple times. - this.body.emitter.emit('startSimulation'); + this.body.emitter.emit("startSimulation"); } }; @@ -27267,23 +27267,23 @@ return /******/ (function(modules) { // webpackBootstrap var _this3 = this; // this event will trigger a rebuilding of the cache everything. Used when nodes or edges have been added or removed. - this.body.emitter.on('_dataChanged', function () { + this.body.emitter.on("_dataChanged", function () { // update shortcut lists _this3._updateVisibleIndices(); _this3.physics.updatePhysicsData(); - _this3.body.emitter.emit('_requestRedraw'); + _this3.body.emitter.emit("_requestRedraw"); // call the dataUpdated event because the only difference between the two is the updating of the indices - _this3.body.emitter.emit('_dataUpdated'); + _this3.body.emitter.emit("_dataUpdated"); }); // this is called when options of EXISTING nodes or edges have changed. - this.body.emitter.on('_dataUpdated', function () { + this.body.emitter.on("_dataUpdated", function () { // update values _this3._updateValueRange(_this3.body.nodes); _this3._updateValueRange(_this3.body.edges); // start simulation (can be called safely, even if already running) - _this3.body.emitter.emit('startSimulation'); - _this3.body.emitter.emit('_requestRedraw'); + _this3.body.emitter.emit("startSimulation"); + _this3.body.emitter.emit("_requestRedraw"); }); }; @@ -27299,8 +27299,8 @@ return /******/ (function(modules) { // webpackBootstrap */ Network.prototype.setData = function (data) { // reset the physics engine. - this.body.emitter.emit('resetPhysics'); - this.body.emitter.emit('_resetData'); + this.body.emitter.emit("resetPhysics"); + this.body.emitter.emit("_resetData"); // unselect all to ensure no selections from old data are carried over. this.selectionHandler.unselectAll(); @@ -27330,13 +27330,13 @@ return /******/ (function(modules) { // webpackBootstrap } // emit change in data - this.body.emitter.emit('_dataChanged'); + this.body.emitter.emit("_dataChanged"); // emit data loaded - this.body.emitter.emit('_dataLoaded'); + this.body.emitter.emit("_dataLoaded"); // find a stable position or start animating to a stable position - this.body.emitter.emit('initPhysics'); + this.body.emitter.emit("initPhysics"); }; /** @@ -27346,7 +27346,7 @@ return /******/ (function(modules) { // webpackBootstrap * network = null; */ Network.prototype.destroy = function () { - this.body.emitter.emit('destroy'); + this.body.emitter.emit("destroy"); // clear events this.body.emitter.off(); this.off(); @@ -27471,7 +27471,7 @@ return /******/ (function(modules) { // webpackBootstrap return this.manipulation.editNode.apply(this.manipulation, arguments); }; Network.prototype.editNodeMode = function () { - console.log('Deprecated: Please use editNode instead of editNodeMode.');return this.manipulation.editNode.apply(this.manipulation, arguments); + console.log("Deprecated: Please use editNode instead of editNodeMode.");return this.manipulation.editNode.apply(this.manipulation, arguments); }; Network.prototype.addEdgeMode = function () { return this.manipulation.addEdgeMode.apply(this.manipulation, arguments); @@ -27642,7 +27642,7 @@ return /******/ (function(modules) { // webpackBootstrap _createClass(Groups, [{ key: "setOptions", value: function setOptions(options) { - var optionFields = ["useDefaultGroups"]; + var optionFields = ['useDefaultGroups']; if (options !== undefined) { for (var groupName in options) { @@ -27655,18 +27655,16 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "clear", /** * Clear all groups */ + }, { + key: "clear", value: function clear() { this.groups = {}; this.groupsArray = []; } - }, { - key: "get", /** * get group options of a groupname. If groupname is not found, a new group @@ -27674,6 +27672,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {*} groupname Can be a number, string, Date, etc. * @return {Object} group The created group, containing all group options */ + }, { + key: "get", value: function get(groupname) { var group = this.groups[groupname]; if (group === undefined) { @@ -27696,8 +27696,6 @@ return /******/ (function(modules) { // webpackBootstrap return group; } - }, { - key: "add", /** * Add a custom group style @@ -27706,6 +27704,8 @@ return /******/ (function(modules) { // webpackBootstrap * backgroundColor, etc. * @return {Object} group The created group object */ + }, { + key: "add", value: function add(groupName, style) { this.groups[groupName] = style; this.groupsArray.push(groupName); @@ -27917,14 +27917,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'setData', /** * Set a data set with nodes for the network * @param {Array | DataSet | DataView} nodes The data containing the nodes. * @private */ + }, { + key: 'setData', value: function setData(nodes) { var _this3 = this; @@ -27968,17 +27968,17 @@ return /******/ (function(modules) { // webpackBootstrap } if (doNotEmit === false) { - this.body.emitter.emit('_dataChanged'); + this.body.emitter.emit("_dataChanged"); } } - }, { - key: 'add', /** * Add nodes * @param {Number[] | String[]} ids * @private */ + }, { + key: 'add', value: function add(ids) { var doNotEmit = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -27995,17 +27995,17 @@ return /******/ (function(modules) { // webpackBootstrap this.layoutEngine.positionInitially(newNodes); if (doNotEmit === false) { - this.body.emitter.emit('_dataChanged'); + this.body.emitter.emit("_dataChanged"); } } - }, { - key: 'update', /** * Update existing nodes, or create them when not yet existing * @param {Number[] | String[]} ids * @private */ + }, { + key: 'update', value: function update(ids, changedData) { var nodes = this.body.nodes; var dataChanged = false; @@ -28024,19 +28024,19 @@ return /******/ (function(modules) { // webpackBootstrap } } if (dataChanged === true) { - this.body.emitter.emit('_dataChanged'); + this.body.emitter.emit("_dataChanged"); } else { - this.body.emitter.emit('_dataUpdated'); + this.body.emitter.emit("_dataUpdated"); } } - }, { - key: 'remove', /** * Remove existing nodes. If nodes do not exist, the method will just ignore it. * @param {Number[] | String[]} ids * @private */ + }, { + key: 'remove', value: function remove(ids) { var nodes = this.body.nodes; @@ -28045,16 +28045,16 @@ return /******/ (function(modules) { // webpackBootstrap delete nodes[id]; } - this.body.emitter.emit('_dataChanged'); + this.body.emitter.emit("_dataChanged"); } - }, { - key: 'create', /** * create a node * @param properties * @param constructorClass */ + }, { + key: 'create', value: function create(properties) { var constructorClass = arguments.length <= 1 || arguments[1] === undefined ? _componentsNode2['default'] : arguments[1]; @@ -28081,14 +28081,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'getPositions', /** * Returns the positions of the nodes. * @param ids --> optional, can be array of nodeIds, can be string * @returns {{}} */ + }, { + key: 'getPositions', value: function getPositions(ids) { var dataArray = {}; if (ids !== undefined) { @@ -28113,12 +28113,12 @@ return /******/ (function(modules) { // webpackBootstrap } return dataArray; } - }, { - key: 'storePositions', /** * Load the XY positions of the nodes into the dataset. */ + }, { + key: 'storePositions', value: function storePositions() { // todo: add support for clusters and hierarchical. var dataArray = []; @@ -28134,27 +28134,27 @@ return /******/ (function(modules) { // webpackBootstrap } dataset.update(dataArray); } - }, { - key: 'getBoundingBox', /** * get the bounding box of a node. * @param nodeId * @returns {j|*} */ + }, { + key: 'getBoundingBox', value: function getBoundingBox(nodeId) { if (this.body.nodes[nodeId] !== undefined) { return this.body.nodes[nodeId].shape.boundingBox; } } - }, { - key: 'getConnectedNodes', /** * Get the Ids of nodes connected to this node. * @param nodeId * @returns {Array} */ + }, { + key: 'getConnectedNodes', value: function getConnectedNodes(nodeId) { var nodeList = []; if (this.body.nodes[nodeId] !== undefined) { @@ -28179,14 +28179,14 @@ return /******/ (function(modules) { // webpackBootstrap } return nodeList; } - }, { - key: 'getConnectedEdges', /** * Get the ids of the edges connected to this node. * @param nodeId * @returns {*} */ + }, { + key: 'getConnectedEdges', value: function getConnectedEdges(nodeId) { var edgeList = []; if (this.body.nodes[nodeId] !== undefined) { @@ -28195,12 +28195,10 @@ return /******/ (function(modules) { // webpackBootstrap edgeList.push(node.edges[i].id); } } else { - console.log('NodeId provided for getConnectedEdges does not exist. Provided: ', nodeId); + console.log("NodeId provided for getConnectedEdges does not exist. Provided: ", nodeId); } return edgeList; } - }, { - key: 'moveNode', /** * Move a node. @@ -28208,6 +28206,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param Number x * @param Number y */ + }, { + key: 'moveNode', value: function moveNode(nodeId, x, y) { var _this4 = this; @@ -28215,10 +28215,10 @@ return /******/ (function(modules) { // webpackBootstrap this.body.nodes[nodeId].x = Number(x); this.body.nodes[nodeId].y = Number(y); setTimeout(function () { - _this4.body.emitter.emit('startSimulation'); + _this4.body.emitter.emit("startSimulation"); }, 0); } else { - console.log('Node id supplied to moveNode does not exist. Provided: ', nodeId); + console.log("Node id supplied to moveNode does not exist. Provided: ", nodeId); } } }]); @@ -28365,39 +28365,39 @@ return /******/ (function(modules) { // webpackBootstrap this.setOptions(options); } + /** + * Attach a edge to the node + * @param {Edge} edge + */ + _createClass(Node, [{ key: 'attachEdge', - - /** - * Attach a edge to the node - * @param {Edge} edge - */ value: function attachEdge(edge) { if (this.edges.indexOf(edge) === -1) { this.edges.push(edge); } } - }, { - key: 'detachEdge', /** * Detach a edge from the node * @param {Edge} edge */ + }, { + key: 'detachEdge', value: function detachEdge(edge) { var index = this.edges.indexOf(edge); if (index != -1) { this.edges.splice(index, 1); } } - }, { - key: 'setOptions', /** * Set or overwrite options for the node * @param {Object} options an object with options * @param {Object} constants and object with default, global options */ + }, { + key: 'setOptions', value: function setOptions(options) { var currentShape = this.options.shape; if (!options) { @@ -28409,7 +28409,7 @@ return /******/ (function(modules) { // webpackBootstrap } if (this.id === undefined) { - throw 'Node must have an id'; + throw "Node must have an id"; } // set these options locally @@ -28451,7 +28451,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this.imagelist) { this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage, this.id); } else { - throw 'No imagelist provided'; + throw "No imagelist provided"; } } @@ -28463,6 +28463,13 @@ return /******/ (function(modules) { // webpackBootstrap } return false; } + + /** + * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined. + * Static so it can also be used by the handler. + * @param parentOptions + * @param newOptions + */ }, { key: 'updateLabelModule', value: function updateLabelModule() { @@ -28531,50 +28538,48 @@ return /******/ (function(modules) { // webpackBootstrap } this._reset(); } - }, { - key: 'select', /** * select this node */ + }, { + key: 'select', value: function select() { this.selected = true; this._reset(); } - }, { - key: 'unselect', /** * unselect this node */ + }, { + key: 'unselect', value: function unselect() { this.selected = false; this._reset(); } - }, { - key: '_reset', /** * Reset the calculated size of the node, forces it to recalculate its size * @private */ + }, { + key: '_reset', value: function _reset() { this.shape.width = undefined; this.shape.height = undefined; } - }, { - key: 'getTitle', /** * get the title of this node. * @return {string} title The title of the node, or undefined when no title * has been set. */ + }, { + key: 'getTitle', value: function getTitle() { return this.options.title; } - }, { - key: 'distanceToBorder', /** * Calculate the distance to the border of the Node @@ -28582,41 +28587,41 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} angle Angle in radians * @returns {number} distance Distance to the border in pixels */ + }, { + key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { return this.shape.distanceToBorder(ctx, angle); } - }, { - key: 'isFixed', /** * Check if this node has a fixed x and y position * @return {boolean} true if fixed, false if not */ + }, { + key: 'isFixed', value: function isFixed() { return this.options.fixed.x && this.options.fixed.y; } - }, { - key: 'isSelected', /** * check if this node is selecte * @return {boolean} selected True if node is selected, else false */ + }, { + key: 'isSelected', value: function isSelected() { return this.selected; } - }, { - key: 'getValue', /** * Retrieve the value of the node. Can be undefined * @return {Number} value */ + }, { + key: 'getValue', value: function getValue() { return this.options.value; } - }, { - key: 'setValueRange', /** * Adjust the value range of the node. The node will adjust it's size @@ -28624,6 +28629,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} min * @param {Number} max */ + }, { + key: 'setValueRange', value: function setValueRange(min, max, total) { if (this.options.value !== undefined) { var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); @@ -28638,68 +28645,61 @@ return /******/ (function(modules) { // webpackBootstrap this.options.font.size = this.baseFontSize; } } - }, { - key: 'draw', /** * Draw this node in the given canvas * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * @param {CanvasRenderingContext2D} ctx */ + }, { + key: 'draw', value: function draw(ctx) { this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); } - }, { - key: 'updateBoundingBox', /** * Update the bounding box of the shape */ + }, { + key: 'updateBoundingBox', value: function updateBoundingBox(ctx) { this.shape.updateBoundingBox(this.x, this.y, ctx); } - }, { - key: 'resize', /** * Recalculate the size of this node in the given canvas * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * @param {CanvasRenderingContext2D} ctx */ + }, { + key: 'resize', value: function resize(ctx) { this.shape.resize(ctx, this.selected); } - }, { - key: 'isOverlappingWith', /** * Check if this object is overlapping with the provided object * @param {Object} obj an object with parameters left, top, right, bottom * @return {boolean} True if location is located on node */ + }, { + key: 'isOverlappingWith', value: function isOverlappingWith(obj) { return this.shape.left < obj.right && this.shape.left + this.shape.width > obj.left && this.shape.top < obj.bottom && this.shape.top + this.shape.height > obj.top; } - }, { - key: 'isBoundingBoxOverlappingWith', /** * Check if this object is overlapping with the provided object * @param {Object} obj an object with parameters left, top, right, bottom * @return {boolean} True if location is located on node */ + }, { + key: 'isBoundingBoxOverlappingWith', value: function isBoundingBoxOverlappingWith(obj) { return this.shape.boundingBox.left < obj.right && this.shape.boundingBox.right > obj.left && this.shape.boundingBox.top < obj.bottom && this.shape.boundingBox.bottom > obj.top; } }], [{ key: 'parseOptions', - - /** - * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined. - * Static so it can also be used by the handler. - * @param parentOptions - * @param newOptions - */ value: function parseOptions(parentOptions, newOptions) { var allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; var globalOptions = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3]; @@ -28839,16 +28839,16 @@ return /******/ (function(modules) { // webpackBootstrap // draw text this._drawText(ctx, selected, x, y, baseline); } - }, { - key: '_drawBackground', /** * Draws the label background * @param {CanvasRenderingContext2D} ctx * @private */ + }, { + key: '_drawBackground', value: function _drawBackground(ctx) { - if (this.fontOptions.background !== undefined && this.fontOptions.background !== 'none') { + if (this.fontOptions.background !== undefined && this.fontOptions.background !== "none") { ctx.fillStyle = this.fontOptions.background; var lineMargin = 2; @@ -28869,8 +28869,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_drawText', /** * @@ -28879,6 +28877,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param baseline * @private */ + }, { + key: '_drawText', value: function _drawText(ctx, selected, x, y) { var baseline = arguments.length <= 4 || arguments[4] === undefined ? 'middle' : arguments[4]; @@ -28898,15 +28898,15 @@ return /******/ (function(modules) { // webpackBootstrap var fontColor = _getColor22[0]; var strokeColor = _getColor22[1]; + // configure context for drawing the text + var _setAlignment2 = this._setAlignment(ctx, x, yLine, baseline); var _setAlignment22 = _slicedToArray(_setAlignment2, 2); x = _setAlignment22[0]; yLine = _setAlignment22[1]; - - // configure context for drawing the text - ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + fontSize + 'px ' + this.fontOptions.face; + ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + fontSize + "px " + this.fontOptions.face; ctx.fillStyle = fontColor; ctx.textAlign = 'center'; @@ -28940,19 +28940,17 @@ return /******/ (function(modules) { // webpackBootstrap ctx.textBaseline = 'alphabetic'; yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers } else if (this.fontOptions.align === 'bottom') { - ctx.textBaseline = 'hanging'; - yLine += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers - } else { - ctx.textBaseline = 'middle'; - } + ctx.textBaseline = 'hanging'; + yLine += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers + } else { + ctx.textBaseline = 'middle'; + } } else { ctx.textBaseline = baseline; } return [x, yLine]; } - }, { - key: '_getColor', /** * fade in when relative scale is between threshold and threshold - 1. @@ -28962,6 +28960,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*[]} * @private */ + }, { + key: '_getColor', value: function _getColor(viewFontSize) { var fontColor = this.fontOptions.color || '#000000'; var strokeColor = this.fontOptions.strokeColor || '#ffffff'; @@ -28972,8 +28972,6 @@ return /******/ (function(modules) { // webpackBootstrap } return [fontColor, strokeColor]; } - }, { - key: 'getTextSize', /** * @@ -28981,6 +28979,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param selected * @returns {{width: number, height: number}} */ + }, { + key: 'getTextSize', value: function getTextSize(ctx) { var selected = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -28991,8 +28991,6 @@ return /******/ (function(modules) { // webpackBootstrap }; return size; } - }, { - key: 'calculateLabelSize', /** * @@ -29002,6 +29000,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param y * @param baseline */ + }, { + key: 'calculateLabelSize', value: function calculateLabelSize(ctx, selected) { var x = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2]; var y = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; @@ -29014,7 +29014,7 @@ return /******/ (function(modules) { // webpackBootstrap this.size.left = x - this.size.width * 0.5; this.size.top = y - this.size.height * 0.5; this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size; - if (baseline === 'hanging') { + if (baseline === "hanging") { this.size.top += 0.5 * this.fontOptions.size; this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers this.size.yLine += 4; // distance from node @@ -29022,8 +29022,6 @@ return /******/ (function(modules) { // webpackBootstrap this.labelDirty = false; } - }, { - key: '_processLabel', /** * This calculates the width as well as explodes the label string and calculates the amount of lines. @@ -29032,6 +29030,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: '_processLabel', value: function _processLabel(ctx, selected) { var width = 0; var lines = ['']; @@ -29039,7 +29039,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this.nodeOptions.label !== undefined) { lines = String(this.nodeOptions.label).split('\n'); lineCount = lines.length; - ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + this.fontOptions.size + 'px ' + this.fontOptions.face; + ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + this.fontOptions.size + "px " + this.fontOptions.face; width = ctx.measureText(lines[0]).width; for (var i = 1; i < lineCount; i++) { var lineWidth = ctx.measureText(lines[i]).width; @@ -29057,8 +29057,8 @@ return /******/ (function(modules) { // webpackBootstrap var allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; if (typeof newOptions.font === 'string') { - var newOptionsArray = newOptions.font.split(' '); - parentOptions.size = newOptionsArray[0].replace('px', ''); + var newOptionsArray = newOptions.font.split(" "); + parentOptions.size = newOptionsArray[0].replace("px", ''); parentOptions.face = newOptionsArray[1]; parentOptions.color = newOptionsArray[2]; } else if (typeof newOptions.font === 'object') { @@ -29092,7 +29092,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -29259,7 +29259,7 @@ return /******/ (function(modules) { // webpackBootstrap } ctx.setLineDash(dashes); } else { - console.warn('setLineDash is not supported in this browser. The dashed borders cannot be used.'); + console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); this.options.shapeProperties.borderDashes = false; } } @@ -29271,7 +29271,7 @@ return /******/ (function(modules) { // webpackBootstrap if (ctx.setLineDash !== undefined) { ctx.setLineDash([0]); } else { - console.warn('setLineDash is not supported in this browser. The dashed borders cannot be used.'); + console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); this.options.shapeProperties.borderDashes = false; } } @@ -29302,7 +29302,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilCircleImageBase = __webpack_require__(67); @@ -29392,7 +29392,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -29417,8 +29417,6 @@ return /******/ (function(modules) { // webpackBootstrap this.imageObj = imageObj; } } - }, { - key: '_resizeImage', /** * This function resizes the image by the options size when the image has not yet loaded. If the image has loaded, we @@ -29426,6 +29424,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: '_resizeImage', value: function _resizeImage() { var force = false; if (!this.imageObj.width || !this.imageObj.height) { @@ -29563,7 +29563,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilCircleImageBase = __webpack_require__(67); @@ -29669,7 +29669,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -29782,7 +29782,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -29838,7 +29838,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -29944,7 +29944,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30001,7 +30001,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -30117,7 +30117,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -30184,12 +30184,12 @@ return /******/ (function(modules) { // webpackBootstrap var iconSize = Number(this.options.icon.size); if (this.options.icon.code !== undefined) { - ctx.font = (selected ? 'bold ' : '') + iconSize + 'px ' + this.options.icon.face; + ctx.font = (selected ? "bold " : "") + iconSize + "px " + this.options.icon.face; // draw icon - ctx.fillStyle = this.options.icon.color || 'black'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; + ctx.fillStyle = this.options.icon.color || "black"; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; // draw shadow if enabled this.enableShadow(ctx); @@ -30232,7 +30232,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilCircleImageBase = __webpack_require__(67); @@ -30352,7 +30352,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30408,7 +30408,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30464,7 +30464,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -30550,7 +30550,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30606,7 +30606,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30756,7 +30756,7 @@ return /******/ (function(modules) { // webpackBootstrap }, smooth: { enabled: true, - type: 'dynamic', + type: "dynamic", forceDirection: 'none', roundness: 0.5 }, @@ -30776,7 +30776,7 @@ return /******/ (function(modules) { // webpackBootstrap var _this2 = this; // this allows external modules to force all dynamic curves to turn static. - this.body.emitter.on('_forceDisableDynamicCurves', function (type) { + this.body.emitter.on("_forceDisableDynamicCurves", function (type) { if (type === 'dynamic') { type = 'continuous'; } @@ -30804,20 +30804,20 @@ return /******/ (function(modules) { // webpackBootstrap } } if (emitChange === true) { - _this2.body.emitter.emit('_dataChanged'); + _this2.body.emitter.emit("_dataChanged"); } }); // this is called when options of EXISTING nodes or edges have changed. - this.body.emitter.on('_dataUpdated', function () { + this.body.emitter.on("_dataUpdated", function () { _this2.reconnectEdges(); _this2.markAllEdgesAsDirty(); }); // refresh the edges. Used when reverting from hierarchical layout - this.body.emitter.on('refreshEdges', this.refresh.bind(this)); - this.body.emitter.on('refresh', this.refresh.bind(this)); - this.body.emitter.on('destroy', function () { + this.body.emitter.on("refreshEdges", this.refresh.bind(this)); + this.body.emitter.on("refresh", this.refresh.bind(this)); + this.body.emitter.on("destroy", function () { delete _this2.body.functions.createEdge; delete _this2.edgesListeners.add; delete _this2.edgesListeners.update; @@ -30864,8 +30864,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'setData', /** * Load edges by reading the data table @@ -30873,6 +30871,8 @@ return /******/ (function(modules) { // webpackBootstrap * @private * @private */ + }, { + key: 'setData', value: function setData(edges) { var _this3 = this; @@ -30915,17 +30915,17 @@ return /******/ (function(modules) { // webpackBootstrap } if (doNotEmit === false) { - this.body.emitter.emit('_dataChanged'); + this.body.emitter.emit("_dataChanged"); } } - }, { - key: 'add', /** * Add edges * @param {Number[] | String[]} ids * @private */ + }, { + key: 'add', value: function add(ids) { var doNotEmit = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -30940,22 +30940,22 @@ return /******/ (function(modules) { // webpackBootstrap oldEdge.disconnect(); } - var data = edgesData.get(id, { 'showInternalIds': true }); + var data = edgesData.get(id, { "showInternalIds": true }); edges[id] = this.create(data); } if (doNotEmit === false) { - this.body.emitter.emit('_dataChanged'); + this.body.emitter.emit("_dataChanged"); } } - }, { - key: 'update', /** * Update existing edges, or create them when not yet existing * @param {Number[] | String[]} ids * @private */ + }, { + key: 'update', value: function update(ids) { var edges = this.body.edges; var edgesData = this.body.data.edges; @@ -30977,19 +30977,19 @@ return /******/ (function(modules) { // webpackBootstrap } if (dataChanged === true) { - this.body.emitter.emit('_dataChanged'); + this.body.emitter.emit("_dataChanged"); } else { - this.body.emitter.emit('_dataUpdated'); + this.body.emitter.emit("_dataUpdated"); } } - }, { - key: 'remove', /** * Remove existing edges. Non existing ids will be ignored * @param {Number[] | String[]} ids * @private */ + }, { + key: 'remove', value: function remove(ids) { var edges = this.body.edges; for (var i = 0; i < ids.length; i++) { @@ -31002,7 +31002,7 @@ return /******/ (function(modules) { // webpackBootstrap } } - this.body.emitter.emit('_dataChanged'); + this.body.emitter.emit("_dataChanged"); } }, { key: 'refresh', @@ -31031,13 +31031,13 @@ return /******/ (function(modules) { // webpackBootstrap this.body.edges[edgeId].edgeType.colorDirty = true; } } - }, { - key: 'reconnectEdges', /** * Reconnect all edges * @private */ + }, { + key: 'reconnectEdges', value: function reconnectEdges() { var id; var nodes = this.body.nodes; @@ -31117,8 +31117,6 @@ return /******/ (function(modules) { // webpackBootstrap var _edgesStraightEdge2 = _interopRequireDefault(_edgesStraightEdge); - var util = __webpack_require__(1); - /** * @class Edge * @@ -31134,13 +31132,14 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Object} constants An object with default values for * example for the color */ + var util = __webpack_require__(1); var Edge = (function () { function Edge(options, body, globalOptions) { _classCallCheck(this, Edge); if (body === undefined) { - throw 'No body provided'; + throw "No body provided"; } this.options = util.bridgeObject(globalOptions); this.globalOptions = globalOptions; @@ -31170,14 +31169,14 @@ return /******/ (function(modules) { // webpackBootstrap this.setOptions(options); } + /** + * Set or overwrite options for the edge + * @param {Object} options an object with options + * @param doNotEmit + */ + _createClass(Edge, [{ key: 'setOptions', - - /** - * Set or overwrite options for the edge - * @param {Object} options an object with options - * @param doNotEmit - */ value: function setOptions(options) { if (!options) { return; @@ -31221,6 +31220,7 @@ return /******/ (function(modules) { // webpackBootstrap } }, { key: 'updateLabelModule', + // this sets the pointer of the option back to the global option. /** * update the options in the label module @@ -31231,13 +31231,13 @@ return /******/ (function(modules) { // webpackBootstrap this.baseFontSize = this.labelModule.baseSize; } } - }, { - key: 'updateEdgeType', /** * update the edge type, set the options * @returns {boolean} */ + }, { + key: 'updateEdgeType', value: function updateEdgeType() { var dataChanged = false; var changeInType = true; @@ -31281,12 +31281,12 @@ return /******/ (function(modules) { // webpackBootstrap return dataChanged; } - }, { - key: 'connect', /** * Connect an edge to its nodes */ + }, { + key: 'connect', value: function connect() { this.disconnect(); @@ -31308,12 +31308,12 @@ return /******/ (function(modules) { // webpackBootstrap this.edgeType.connect(); } - }, { - key: 'disconnect', /** * Disconnect an edge from its nodes */ + }, { + key: 'disconnect', value: function disconnect() { if (this.from) { this.from.detachEdge(this); @@ -31326,39 +31326,37 @@ return /******/ (function(modules) { // webpackBootstrap this.connected = false; } - }, { - key: 'getTitle', /** * get the title of this edge. * @return {string} title The title of the edge, or undefined when no title * has been set. */ + }, { + key: 'getTitle', value: function getTitle() { return this.title; } - }, { - key: 'isSelected', /** * check if this node is selecte * @return {boolean} selected True if node is selected, else false */ + }, { + key: 'isSelected', value: function isSelected() { return this.selected; } - }, { - key: 'getValue', /** * Retrieve the value of the edge. Can be undefined * @return {Number} value */ + }, { + key: 'getValue', value: function getValue() { return this.options.value; } - }, { - key: 'setValueRange', /** * Adjust the value range of the edge. The edge will adjust it's width @@ -31367,6 +31365,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} max * @param total */ + }, { + key: 'setValueRange', value: function setValueRange(min, max, total) { if (this.options.value !== undefined) { var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); @@ -31398,8 +31398,6 @@ return /******/ (function(modules) { // webpackBootstrap this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width; } } - }, { - key: 'draw', /** * Redraw a edge @@ -31407,6 +31405,8 @@ return /******/ (function(modules) { // webpackBootstrap * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * @param {CanvasRenderingContext2D} ctx */ + }, { + key: 'draw', value: function draw(ctx) { var via = this.edgeType.drawLine(ctx, this.selected, this.hover); this.drawArrows(ctx, via); @@ -31439,7 +31439,7 @@ return /******/ (function(modules) { // webpackBootstrap ctx.save(); // if the label has to be rotated: - if (this.options.font.align !== 'horizontal') { + if (this.options.font.align !== "horizontal") { this.labelModule.calculateLabelSize(ctx, selected, point.x, point.y); ctx.translate(point.x, this.labelModule.size.yLine); this._rotateForLabelAlignment(ctx); @@ -31465,14 +31465,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'isOverlappingWith', /** * Check if this object is overlapping with the provided object * @param {Object} obj an object with parameters left, top * @return {boolean} True if location is located on the edge */ + }, { + key: 'isOverlappingWith', value: function isOverlappingWith(obj) { if (this.connected) { var distMax = 10; @@ -31490,14 +31490,14 @@ return /******/ (function(modules) { // webpackBootstrap return false; } } - }, { - key: '_rotateForLabelAlignment', /** * Rotates the canvas so the text is most readable * @param {CanvasRenderingContext2D} ctx * @private */ + }, { + key: '_rotateForLabelAlignment', value: function _rotateForLabelAlignment(ctx) { var dy = this.from.y - this.to.y; var dx = this.from.x - this.to.x; @@ -31510,8 +31510,6 @@ return /******/ (function(modules) { // webpackBootstrap ctx.rotate(angleInDegrees); } - }, { - key: '_pointOnCircle', /** * Get a point on a circle @@ -31522,6 +31520,8 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Object} point * @private */ + }, { + key: '_pointOnCircle', value: function _pointOnCircle(x, y, radius, percentage) { var angle = percentage * 2 * Math.PI; return { @@ -31539,13 +31539,13 @@ return /******/ (function(modules) { // webpackBootstrap value: function unselect() { this.selected = false; } - }, { - key: 'cleanup', /** * cleans all required things on delete * @returns {*} */ + }, { + key: 'cleanup', value: function cleanup() { return this.edgeType.cleanup(); } @@ -31586,13 +31586,13 @@ return /******/ (function(modules) { // webpackBootstrap if (newOptions.arrows !== undefined && newOptions.arrows !== null) { if (typeof newOptions.arrows === 'string') { var arrows = newOptions.arrows.toLowerCase(); - if (arrows.indexOf('to') != -1) { + if (arrows.indexOf("to") != -1) { parentOptions.arrows.to.enabled = true; } - if (arrows.indexOf('middle') != -1) { + if (arrows.indexOf("middle") != -1) { parentOptions.arrows.middle.enabled = true; } - if (arrows.indexOf('from') != -1) { + if (arrows.indexOf("from") != -1) { parentOptions.arrows.from.enabled = true; } } else if (typeof newOptions.arrows === 'object') { @@ -31600,7 +31600,7 @@ return /******/ (function(modules) { // webpackBootstrap util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'middle', allowDeletion, globalOptions.arrows); util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'from', allowDeletion, globalOptions.arrows); } else { - throw new Error('The arrow newOptions can only be an object or a string. Refer to the documentation. You used:' + JSON.stringify(newOptions.arrows)); + throw new Error("The arrow newOptions can only be an object or a string. Refer to the documentation. You used:" + JSON.stringify(newOptions.arrows)); } } else if (allowDeletion === true && newOptions.arrows === null) { parentOptions.arrows = Object.create(globalOptions.arrows); // this sets the pointer of the option back to the global option. @@ -31643,7 +31643,7 @@ return /******/ (function(modules) { // webpackBootstrap if (newOptions.font !== undefined && newOptions.font !== null) { _sharedLabel2['default'].parseOptions(parentOptions.font, newOptions); } else if (allowDeletion === true && newOptions.font === null) { - parentOptions.font = Object.create(globalOptions.font); // this sets the pointer of the option back to the global option. + parentOptions.font = Object.create(globalOptions.font); } } }]); @@ -31674,7 +31674,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilCubicBezierEdgeBase = __webpack_require__(84); @@ -31689,14 +31689,14 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(CubicBezierEdge.prototype), 'constructor', this).call(this, options, body, labelModule); } + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + _createClass(CubicBezierEdge, [{ key: '_line', - - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ value: function _line(ctx) { // get the coordinates of the support points. @@ -31770,8 +31770,6 @@ return /******/ (function(modules) { // webpackBootstrap // x3,y3 is the point return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2); } - }, { - key: 'getPoint', /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way @@ -31780,6 +31778,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ + }, { + key: 'getPoint', value: function getPoint(percentage) { var _ref3 = arguments.length <= 1 || arguments[1] === undefined ? this._getViaCoordinates() : arguments[1]; @@ -31825,7 +31825,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _BezierEdgeBase2 = __webpack_require__(85); @@ -31840,22 +31840,22 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(CubicBezierEdgeBase.prototype), 'constructor', this).call(this, options, body, labelModule); } + /** + * Calculate the distance between a point (x3,y3) and a line segment from + * (x1,y1) to (x2,y2). + * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment + * https://en.wikipedia.org/wiki/B%C3%A9zier_curve + * @param {number} x1 from x + * @param {number} y1 from y + * @param {number} x2 to x + * @param {number} y2 to y + * @param {number} x3 point to check x + * @param {number} y3 point to check y + * @private + */ + _createClass(CubicBezierEdgeBase, [{ key: '_getDistanceToBezierEdge', - - /** - * Calculate the distance between a point (x3,y3) and a line segment from - * (x1,y1) to (x2,y2). - * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment - * https://en.wikipedia.org/wiki/B%C3%A9zier_curve - * @param {number} x1 from x - * @param {number} y1 from y - * @param {number} x2 to x - * @param {number} y2 to y - * @param {number} x3 point to check x - * @param {number} y3 point to check y - * @private - */ value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2) { // x3,y3 is the point var minDistance = 1e9; @@ -31911,7 +31911,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _EdgeBase2 = __webpack_require__(86); @@ -31926,22 +31926,22 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(BezierEdgeBase.prototype), 'constructor', this).call(this, options, body, labelModule); } + /** + * This function uses binary search to look for the point where the bezier curve crosses the border of the node. + * + * @param nearNode + * @param ctx + * @param viaNode + * @param nearNode + * @param ctx + * @param viaNode + * @param nearNode + * @param ctx + * @param viaNode + */ + _createClass(BezierEdgeBase, [{ key: '_findBorderPositionBezier', - - /** - * This function uses binary search to look for the point where the bezier curve crosses the border of the node. - * - * @param nearNode - * @param ctx - * @param viaNode - * @param nearNode - * @param ctx - * @param viaNode - * @param nearNode - * @param ctx - * @param viaNode - */ value: function _findBorderPositionBezier(nearNode, ctx) { var viaNode = arguments.length <= 2 || arguments[2] === undefined ? this._getViaCoordinates() : arguments[2]; @@ -31969,19 +31969,19 @@ return /******/ (function(modules) { // webpackBootstrap if (Math.abs(difference) < threshold) { break; // found } else if (difference < 0) { - // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (from === false) { - low = middle; - } else { - high = middle; - } - } else { - if (from === false) { - high = middle; + // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (from === false) { + low = middle; + } else { + high = middle; + } } else { - low = middle; + if (from === false) { + high = middle; + } else { + low = middle; + } } - } iteration++; } @@ -31989,8 +31989,6 @@ return /******/ (function(modules) { // webpackBootstrap return pos; } - }, { - key: '_getDistanceToBezierEdge', /** * Calculate the distance between a point (x3,y3) and a line segment from @@ -32004,6 +32002,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {number} y3 point to check y * @private */ + }, { + key: '_getDistanceToBezierEdge', value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via) { // x3,y3 is the point var minDistance = 1e9; @@ -32086,8 +32086,6 @@ return /******/ (function(modules) { // webpackBootstrap this.to = this.body.nodes[this.options.to]; this.id = this.options.id; } - }, { - key: 'drawLine', /** * Redraw a edge as a line @@ -32096,6 +32094,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {CanvasRenderingContext2D} ctx * @private */ + }, { + key: 'drawLine', value: function drawLine(ctx, selected, hover) { // set style ctx.strokeStyle = this.getColor(ctx, selected, hover); @@ -32247,8 +32247,6 @@ return /******/ (function(modules) { // webpackBootstrap } return [x, y, radius]; } - }, { - key: '_pointOnCircle', /** * Get a point on a circle @@ -32259,6 +32257,8 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Object} point * @private */ + }, { + key: '_pointOnCircle', value: function _pointOnCircle(x, y, radius, percentage) { var angle = percentage * 2 * Math.PI; return { @@ -32266,8 +32266,6 @@ return /******/ (function(modules) { // webpackBootstrap y: y - radius * Math.sin(angle) }; } - }, { - key: '_findBorderPositionCircle', /** * This function uses binary search to look for the point where the circle crosses the border of the node. @@ -32277,6 +32275,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ + }, { + key: '_findBorderPositionCircle', value: function _findBorderPositionCircle(node, ctx, options) { var x = options.x; var y = options.y; @@ -32306,27 +32306,25 @@ return /******/ (function(modules) { // webpackBootstrap if (Math.abs(difference) < threshold) { break; // found } else if (difference > 0) { - // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (direction > 0) { - low = middle; - } else { - high = middle; - } - } else { - if (direction > 0) { - high = middle; + // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (direction > 0) { + low = middle; + } else { + high = middle; + } } else { - low = middle; + if (direction > 0) { + high = middle; + } else { + low = middle; + } } - } iteration++; } pos.t = middle; return pos; } - }, { - key: 'getLineWidth', /** * Get the line width of the edge. Depends on width and whether one of the @@ -32334,6 +32332,8 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number} width * @private */ + }, { + key: 'getLineWidth', value: function getLineWidth(selected, hover) { if (selected === true) { return Math.max(this.selectionWidth, 0.3 / this.body.view.scale); @@ -32374,7 +32374,7 @@ return /******/ (function(modules) { // webpackBootstrap } if (this.colorDirty === true) { - if (colorOptions.inherit === 'to') { + if (colorOptions.inherit === "to") { this.color.highlight = this.to.options.color.highlight.border; this.color.hover = this.to.options.color.hover.border; this.color.color = util.overrideOpacity(this.to.options.color.border, colorOptions.opacity); @@ -32402,8 +32402,6 @@ return /******/ (function(modules) { // webpackBootstrap return this.color.color; } } - }, { - key: '_circle', /** * Draw a line from a node to itself, a circle @@ -32413,6 +32411,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} radius * @private */ + }, { + key: '_circle', value: function _circle(ctx, x, y, radius) { // draw shadow if enabled this.enableShadow(ctx); @@ -32425,8 +32425,6 @@ return /******/ (function(modules) { // webpackBootstrap // disable shadows for other elements. this.disableShadow(ctx); } - }, { - key: 'getDistanceToEdge', /** * Calculate the distance between a point (x3,y3) and a line segment from @@ -32440,6 +32438,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {number} y3 * @private */ + }, { + key: 'getDistanceToEdge', value: function getDistanceToEdge(x1, y1, x2, y2, x3, y3, via) { // x3,y3 is the point var returnValue = 0; @@ -32492,8 +32492,6 @@ return /******/ (function(modules) { // webpackBootstrap return Math.sqrt(dx * dx + dy * dy); } - }, { - key: 'drawArrowHead', /** * @@ -32501,6 +32499,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param position * @param viaNode */ + }, { + key: 'drawArrowHead', value: function drawArrowHead(ctx, position, viaNode, selected, hover) { // set style ctx.strokeStyle = this.getColor(ctx, selected, hover); @@ -32642,7 +32642,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilBezierEdgeBase = __webpack_require__(85); @@ -32694,13 +32694,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "cleanup", /** * remove the support nodes * @returns {boolean} */ + }, { + key: "cleanup", value: function cleanup() { this.body.emitter.off("_repositionBezierNodes", this._boundFunction); if (this.via !== undefined) { @@ -32710,8 +32710,6 @@ return /******/ (function(modules) { // webpackBootstrap } return false; } - }, { - key: "setupSupportNode", /** * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but @@ -32720,12 +32718,14 @@ return /******/ (function(modules) { // webpackBootstrap * The changed data is not called, if needed, it is returned by the main edge constructor. * @private */ + }, { + key: "setupSupportNode", value: function setupSupportNode() { if (this.via === undefined) { var nodeId = "edgeId:" + this.id; var node = this.body.functions.createNode({ id: nodeId, - shape: "circle", + shape: 'circle', physics: true, hidden: true }); @@ -32746,14 +32746,14 @@ return /******/ (function(modules) { // webpackBootstrap this.via.y = 0; } } - }, { - key: "_line", /** * Draw a line between two nodes * @param {CanvasRenderingContext2D} ctx * @private */ + }, { + key: "_line", value: function _line(ctx) { // draw a straight line ctx.beginPath(); @@ -32765,8 +32765,6 @@ return /******/ (function(modules) { // webpackBootstrap this.disableShadow(ctx); return this.via; } - }, { - key: "getPoint", /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way @@ -32775,6 +32773,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ + }, { + key: "getPoint", value: function getPoint(percentage) { var t = percentage; var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * this.via.x + Math.pow(t, 2) * this.to.x; @@ -32819,7 +32819,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilBezierEdgeBase = __webpack_require__(85); @@ -32834,14 +32834,14 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(BezierEdgeStatic.prototype), 'constructor', this).call(this, options, body, labelModule); } + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + _createClass(BezierEdgeStatic, [{ key: '_line', - - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ value: function _line(ctx) { // draw a straight line ctx.beginPath(); @@ -32890,7 +32890,7 @@ return /******/ (function(modules) { // webpackBootstrap yVia = this.from.y + factor * dy; } } - if (type === 'discrete') { + if (type === "discrete") { xVia = dx < factor * dy ? this.from.x : xVia; } } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { @@ -32911,11 +32911,11 @@ return /******/ (function(modules) { // webpackBootstrap yVia = this.from.y + factor * dx; } } - if (type === 'discrete') { + if (type === "discrete") { yVia = dy < factor * dx ? this.from.y : yVia; } } - } else if (type === 'straightCross') { + } else if (type === "straightCross") { if (Math.abs(this.from.x - this.to.x) <= Math.abs(this.from.y - this.to.y)) { // up - down xVia = this.from.x; @@ -33033,8 +33033,6 @@ return /******/ (function(modules) { // webpackBootstrap // x3,y3 is the point return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via); } - }, { - key: 'getPoint', /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way @@ -33043,6 +33041,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ + }, { + key: 'getPoint', value: function getPoint(percentage) { var via = arguments.length <= 1 || arguments[1] === undefined ? this._getViaCoordinates() : arguments[1]; @@ -33078,7 +33078,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _utilEdgeBase = __webpack_require__(86); @@ -33093,14 +33093,14 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(StraightEdge.prototype), 'constructor', this).call(this, options, body, labelModule); } + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + _createClass(StraightEdge, [{ key: '_line', - - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ value: function _line(ctx) { // draw a straight line ctx.beginPath(); @@ -33112,8 +33112,6 @@ return /******/ (function(modules) { // webpackBootstrap this.disableShadow(ctx); return undefined; } - }, { - key: 'getPoint', /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way @@ -33122,6 +33120,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ + }, { + key: 'getPoint', value: function getPoint(percentage) { return { x: (1 - percentage) * this.from.x + percentage * this.to.x, @@ -33213,6 +33213,10 @@ return /******/ (function(modules) { // webpackBootstrap var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver); + var _workerPhysicsWorkerWrapper = __webpack_require__(99); + + var _workerPhysicsWorkerWrapper2 = _interopRequireDefault(_workerPhysicsWorkerWrapper); + var util = __webpack_require__(1); var PhysicsEngine = (function () { @@ -33245,6 +33249,7 @@ return /******/ (function(modules) { // webpackBootstrap this.options = {}; this.defaultOptions = { enabled: true, + useWorker: false, barnesHut: { theta: 0.5, gravitationalConstant: -2000, @@ -33294,6 +33299,7 @@ return /******/ (function(modules) { // webpackBootstrap util.extend(this.options, this.defaultOptions); this.timestep = 0.5; this.layoutFailed = false; + this.draggingNodes = []; this.bindEventListeners(); } @@ -33333,14 +33339,27 @@ return /******/ (function(modules) { // webpackBootstrap _this.stopSimulation(false); _this.body.emitter.off(); }); + // For identifying which nodes to send to worker thread + this.body.emitter.on('dragStart', function (properties) { + _this.draggingNodes = properties.nodes; + }); + this.body.emitter.on('dragEnd', function () { + _this.draggingNodes = []; + }); + this.body.emitter.on('destroy', function () { + if (_this.physicsWorker) { + _this.physicsWorker.terminate(); + _this.physicsWorker = undefined; + } + }); } - }, { - key: 'setOptions', /** * set the physics options * @param options */ + }, { + key: 'setOptions', value: function setOptions(options) { if (options !== undefined) { if (options === false) { @@ -33365,15 +33384,26 @@ return /******/ (function(modules) { // webpackBootstrap this.timestep = this.options.timestep; } } - this.init(); + if (this.options.useWorker) { + this.initPhysicsWorker(); + this.physicsWorker.postMessage({ type: 'options', data: this.options }); + } else { + this.initEmbeddedPhysics(); + } } - }, { - key: 'init', /** * configure the engine. */ - value: function init() { + }, { + key: 'initEmbeddedPhysics', + value: function initEmbeddedPhysics() { + if (this.physicsWorker) { + this.options.useWorker = false; + this.physicsWorker.terminate(); + this.physicsWorker = undefined; + this.updatePhysicsData(); + } var options; if (this.options.solver === 'forceAtlas2Based') { options = this.options.forceAtlas2Based; @@ -33401,11 +33431,75 @@ return /******/ (function(modules) { // webpackBootstrap this.modelOptions = options; } }, { - key: 'initPhysics', + key: 'initPhysicsWorker', + value: function initPhysicsWorker() { + var _this2 = this; + + if (!this.physicsWorker) { + if (!__webpack_require__.p) { + var parentScript = document.getElementById('visjs'); + if (parentScript) { + var src = parentScript.getAttribute('src'); + __webpack_require__.p = src.substr(0, src.lastIndexOf('/') + 1); + } else { + var scripts = document.getElementsByTagName('script'); + for (var i = 0; i < scripts.length; i++) { + var src = scripts[i].getAttribute('src'); + if (src && src.length >= 6) { + var position = src.length - 6; + var index = src.indexOf('vis.js', position); + if (index === position) { + __webpack_require__.p = src.substr(0, src.lastIndexOf('/') + 1); + break; + } + } + } + } + } + this.physicsWorker = new _workerPhysicsWorkerWrapper2['default'](); + this.physicsWorker.addEventListener('message', function (event) { + _this2.physicsWorkerMessageHandler(event); + }); + this.physicsWorker.onerror = function (event) { + console.error('Falling back to embedded physics engine'); + _this2.initEmbeddedPhysics(); + // throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")"); + }; + } + } + }, { + key: 'physicsWorkerMessageHandler', + value: function physicsWorkerMessageHandler(event) { + var msg = event.data; + switch (msg.type) { + case 'positions': + this.stabilized = msg.data.stabilized; + var positions = msg.data.positions; + // console.log('received positions', positions); + for (var i = 0; i < this.draggingNodes; i++) { + delete positions[this.draggingNodes[i]]; + } + var nodeIds = Object.keys(positions); + for (var i = 0; i < nodeIds.length; i++) { + var nodeId = nodeIds[i]; + var node = this.body.nodes[nodeId]; + // handle case where we get a positions from an old physicsObject + if (node) { + node.x = positions[nodeId].x; + node.y = positions[nodeId].y; + } + } + break; + default: + console.warn('unhandled physics worker message:', msg); + } + } /** * initialize the engine */ + }, { + key: 'initPhysics', value: function initPhysics() { if (this.physicsEnabled === true && this.options.enabled === true) { if (this.options.stabilization.enabled === true) { @@ -33421,21 +33515,35 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('fit'); } } - }, { - key: 'startSimulation', /** * Start the simulation */ + }, { + key: 'startSimulation', value: function startSimulation() { if (this.physicsEnabled === true && this.options.enabled === true) { + if (this.physicsWorker) { + for (var i = 0; i < this.draggingNodes.length; i++) { + var nodeId = this.draggingNodes[i]; + var node = this.body.nodes[nodeId]; + this.physicsWorker.postMessage({ + type: 'update', + data: { + id: nodeId, + x: node.x, + y: node.y + } + }); + } + } this.stabilized = false; // when visible, adaptivity is disabled. this.adaptiveTimestep = false; // this sets the width of all nodes initially which could be required for the avoidOverlap - this.body.emitter.emit('_resizeNodes'); + this.body.emitter.emit("_resizeNodes"); if (this.viewFunction === undefined) { this.viewFunction = this.simulationStep.bind(this); this.body.emitter.on('initRedraw', this.viewFunction); @@ -33445,12 +33553,12 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_redraw'); } } - }, { - key: 'stopSimulation', /** * Stop the simulation, force stabilization. */ + }, { + key: 'stopSimulation', value: function stopSimulation() { var emit = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0]; @@ -33466,13 +33574,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'simulationStep', /** * The viewFunction inserts this step into each renderloop. It calls the physics tick and handles the cleanup at stabilized. * */ + }, { + key: 'simulationStep', value: function simulationStep() { // check if the physics have settled var startTime = Date.now(); @@ -33491,34 +33599,34 @@ return /******/ (function(modules) { // webpackBootstrap this.stopSimulation(); } } - }, { - key: '_emitStabilized', /** * trigger the stabilized event. * @private */ + }, { + key: '_emitStabilized', value: function _emitStabilized() { - var _this2 = this; + var _this3 = this; var amountOfIterations = arguments.length <= 0 || arguments[0] === undefined ? this.stabilizationIterations : arguments[0]; if (this.stabilizationIterations > 1 || this.startedStabilization === true) { setTimeout(function () { - _this2.body.emitter.emit('stabilized', { iterations: amountOfIterations }); - _this2.startedStabilization = false; - _this2.stabilizationIterations = 0; + _this3.body.emitter.emit('stabilized', { iterations: amountOfIterations }); + _this3.startedStabilization = false; + _this3.stabilizationIterations = 0; }, 0); } } - }, { - key: 'physicsTick', /** * A single simulation step (or 'tick') in the physics simulation * * @private */ + }, { + key: 'physicsTick', value: function physicsTick() { // this is here to ensure that there is no start event when the network is already stable. if (this.startedStabilization === false) { @@ -33577,8 +33685,13 @@ return /******/ (function(modules) { // webpackBootstrap } else { // case for the static timestep, we reset it to the one in options and take a normal step. this.timestep = this.options.timestep; - this.calculateForces(); - this.moveNodes(); + if (this.physicsWorker) { + // console.log('asking working to do a physics iteration'); + this.physicsWorker.postMessage({ type: 'calculateForces' }); + } else { + this.calculateForces(); + this.moveNodes(); + } } // determine if the network has stabilzied @@ -33589,63 +33702,128 @@ return /******/ (function(modules) { // webpackBootstrap this.stabilizationIterations++; } } - }, { - key: 'updatePhysicsData', /** * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. * * @private */ + }, { + key: 'updatePhysicsData', value: function updatePhysicsData() { - this.physicsBody.forces = {}; - this.physicsBody.physicsNodeIndices = []; - this.physicsBody.physicsEdgeIndices = []; var nodes = this.body.nodes; var edges = this.body.edges; - // get node indices for physics - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].options.physics === true) { - this.physicsBody.physicsNodeIndices.push(nodeId); + if (this.physicsWorker) { + var physicsWorkerNodes = {}; + var physicsWorkerEdges = {}; + + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + var node = nodes[nodeId]; + if (node.options.physics === true) { + physicsWorkerNodes[nodeId] = { + id: node.id, + x: node.x, + y: node.y, + options: { + fixed: { + x: node.options.fixed.x, + y: node.options.fixed.y + }, + mass: node.options.mass + } + }; + } } } - } - // get edge indices for physics - for (var edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].options.physics === true) { - this.physicsBody.physicsEdgeIndices.push(edgeId); + for (var edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + var edge = edges[edgeId]; + if (edge.options.physics === true) { + physicsWorkerEdges[edgeId] = { + connected: edge.connected, + id: edge.id, + edgeType: {}, + toId: edge.toId, + fromId: edge.fromId, + to: { + id: edge.to.id + }, + from: { + id: edge.from.id + }, + options: { + length: edge.length + } + }; + if (edge.edgeType.via) { + physicsWorkerEdges[edgeId].edgeType = { + via: { + id: edge.edgeType.via.id + } + }; + } + } + } + } + + this.physicsWorker.postMessage({ + type: 'physicsObjects', + data: { + nodes: physicsWorkerNodes, + edges: physicsWorkerEdges + } + }); + } else { + this.physicsBody.forces = {}; + this.physicsBody.physicsNodeIndices = []; + this.physicsBody.physicsEdgeIndices = []; + + // get node indices for physics + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + if (nodes[nodeId].options.physics === true) { + this.physicsBody.physicsNodeIndices.push(nodeId); + } + } + } + + // get edge indices for physics + for (var edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + if (edges[edgeId].options.physics === true) { + this.physicsBody.physicsEdgeIndices.push(edgeId); + } } } - } - // get the velocity and the forces vector - for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { - var nodeId = this.physicsBody.physicsNodeIndices[i]; - this.physicsBody.forces[nodeId] = { x: 0, y: 0 }; + // get the velocity and the forces vector + for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { + var nodeId = this.physicsBody.physicsNodeIndices[i]; + this.physicsBody.forces[nodeId] = { x: 0, y: 0 }; - // forces can be reset because they are recalculated. Velocities have to persist. - if (this.physicsBody.velocities[nodeId] === undefined) { - this.physicsBody.velocities[nodeId] = { x: 0, y: 0 }; + // forces can be reset because they are recalculated. Velocities have to persist. + if (this.physicsBody.velocities[nodeId] === undefined) { + this.physicsBody.velocities[nodeId] = { x: 0, y: 0 }; + } } - } - // clean deleted nodes from the velocity vector - for (var nodeId in this.physicsBody.velocities) { - if (nodes[nodeId] === undefined) { - delete this.physicsBody.velocities[nodeId]; + // clean deleted nodes from the velocity vector + for (var nodeId in this.physicsBody.velocities) { + if (nodes[nodeId] === undefined) { + delete this.physicsBody.velocities[nodeId]; + } } } } - }, { - key: 'revert', /** * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. */ + }, { + key: 'revert', value: function revert() { var nodeIds = Object.keys(this.previousStates); var nodes = this.body.nodes; @@ -33669,12 +33847,12 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_evaluateStepQuality', /** * This compares the reference state to the current state */ + }, { + key: '_evaluateStepQuality', value: function _evaluateStepQuality() { var dx = undefined, dy = undefined, @@ -33697,13 +33875,13 @@ return /******/ (function(modules) { // webpackBootstrap } return true; } - }, { - key: 'moveNodes', /** * move the nodes one timestap and check if they are stabilized * @returns {boolean} */ + }, { + key: 'moveNodes', value: function moveNodes() { var nodeIndices = this.physicsBody.physicsNodeIndices; var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; @@ -33725,8 +33903,6 @@ return /******/ (function(modules) { // webpackBootstrap this.adaptiveTimestepEnabled = averageNodeVelocity / nodeIndices.length < velocityAdaptiveThreshold; this.stabilized = maxNodeVelocity < this.options.minVelocity; } - }, { - key: '_performStep', /** * Perform the actual step @@ -33736,6 +33912,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: '_performStep', value: function _performStep(nodeId, maxVelocity) { var node = this.body.nodes[nodeId]; var timestep = this.timestep; @@ -33752,9 +33930,9 @@ return /******/ (function(modules) { // webpackBootstrap velocities[nodeId].x = Math.abs(velocities[nodeId].x) > maxVelocity ? velocities[nodeId].x > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].x; node.x += velocities[nodeId].x * timestep; // position } else { - forces[nodeId].x = 0; - velocities[nodeId].x = 0; - } + forces[nodeId].x = 0; + velocities[nodeId].x = 0; + } if (node.options.fixed.y === false) { var dy = this.modelOptions.damping * velocities[nodeId].y; // damping force @@ -33763,26 +33941,24 @@ return /******/ (function(modules) { // webpackBootstrap velocities[nodeId].y = Math.abs(velocities[nodeId].y) > maxVelocity ? velocities[nodeId].y > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].y; node.y += velocities[nodeId].y * timestep; // position } else { - forces[nodeId].y = 0; - velocities[nodeId].y = 0; - } + forces[nodeId].y = 0; + velocities[nodeId].y = 0; + } var totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x, 2) + Math.pow(velocities[nodeId].y, 2)); return totalVelocity; } - }, { - key: 'calculateForces', /** * calculate the forces for one physics iteration. */ + }, { + key: 'calculateForces', value: function calculateForces() { this.gravitySolver.solve(); this.nodesSolver.solve(); this.edgesSolver.solve(); } - }, { - key: '_freezeNodes', /** * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization @@ -33790,6 +33966,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: '_freezeNodes', value: function _freezeNodes() { var nodes = this.body.nodes; for (var id in nodes) { @@ -33802,14 +33980,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_restoreFrozenNodes', /** * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. * * @private */ + }, { + key: '_restoreFrozenNodes', value: function _restoreFrozenNodes() { var nodes = this.body.nodes; for (var id in nodes) { @@ -33822,15 +34000,15 @@ return /******/ (function(modules) { // webpackBootstrap } this.freezeCache = {}; } - }, { - key: 'stabilize', /** * Find a stable position for all nodes * @private */ + }, { + key: 'stabilize', value: function stabilize() { - var _this3 = this; + var _this4 = this; var iterations = arguments.length <= 0 || arguments[0] === undefined ? this.options.stabilization.iterations : arguments[0]; @@ -33848,7 +34026,7 @@ return /******/ (function(modules) { // webpackBootstrap this.adaptiveTimestep = true && this.options.adaptiveTimestep; // this sets the width of all nodes initially which could be required for the avoidOverlap - this.body.emitter.emit('_resizeNodes'); + this.body.emitter.emit("_resizeNodes"); // stop the render loop this.stopSimulation(); @@ -33867,16 +34045,16 @@ return /******/ (function(modules) { // webpackBootstrap this.stabilizationIterations = 0; setTimeout(function () { - return _this3._stabilizationBatch(); + return _this4._stabilizationBatch(); }, 0); } - }, { - key: '_stabilizationBatch', /** * One batch of stabilization * @private */ + }, { + key: '_stabilizationBatch', value: function _stabilizationBatch() { // this is here to ensure that there is at least one start event. if (this.startedStabilization === false) { @@ -33897,13 +34075,13 @@ return /******/ (function(modules) { // webpackBootstrap this._finalizeStabilization(); } } - }, { - key: '_finalizeStabilization', /** * Wrap up the stabilization, fit and emit the events. * @private */ + }, { + key: '_finalizeStabilization', value: function _finalizeStabilization() { this.body.emitter.emit('_allowRedraw'); if (this.options.stabilization.fit === true) { @@ -33971,8 +34149,6 @@ return /******/ (function(modules) { // webpackBootstrap var x = Math.sin(this.randomSeed++) * 10000; return x - Math.floor(x); } - }, { - key: "solve", /** * This function calculates the forces the nodes apply on eachother based on a gravitational model. @@ -33980,6 +34156,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: "solve", value: function solve() { if (this.options.gravitationalConstant !== 0 && this.physicsBody.physicsNodeIndices.length > 0) { var node = undefined; @@ -34006,8 +34184,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "_getForceContribution", /** * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. @@ -34017,6 +34193,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param node * @private */ + }, { + key: "_getForceContribution", value: function _getForceContribution(parentBranch, node) { // we get no force contribution from an empty region if (parentBranch.childrenCount > 0) { @@ -34051,8 +34229,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "_calculateForces", /** * Calculate the forces based on the distance. @@ -34064,6 +34240,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentBranch * @private */ + }, { + key: "_calculateForces", value: function _calculateForces(distance, dx, dy, node, parentBranch) { if (distance === 0) { distance = 0.1; @@ -34083,8 +34261,6 @@ return /******/ (function(modules) { // webpackBootstrap this.physicsBody.forces[node.id].x += fx; this.physicsBody.forces[node.id].y += fy; } - }, { - key: "_formBarnesHutTree", /** * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. @@ -34093,6 +34269,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param nodeIndices * @private */ + }, { + key: "_formBarnesHutTree", value: function _formBarnesHutTree(nodes, nodeIndices) { var node = undefined; var nodeCount = nodeIndices.length; @@ -34128,9 +34306,9 @@ return /******/ (function(modules) { // webpackBootstrap maxY += 0.5 * sizeDiff; } // xSize > ySize else { - minX += 0.5 * sizeDiff; - maxX -= 0.5 * sizeDiff; - } // xSize < ySize + minX += 0.5 * sizeDiff; + maxX -= 0.5 * sizeDiff; + } // xSize < ySize var minimumTreeSize = 1e-5; var rootSize = Math.max(minimumTreeSize, Math.abs(maxX - minX)); @@ -34168,8 +34346,6 @@ return /******/ (function(modules) { // webpackBootstrap // make global return barnesHutTree; } - }, { - key: "_updateBranchMass", /** * this updates the mass of a branch. this is increased by adding a node. @@ -34178,6 +34354,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param node * @private */ + }, { + key: "_updateBranchMass", value: function _updateBranchMass(parentBranch, node) { var totalMass = parentBranch.mass + node.options.mass; var totalMassInv = 1 / totalMass; @@ -34192,8 +34370,6 @@ return /******/ (function(modules) { // webpackBootstrap var biggestSize = Math.max(Math.max(node.height, node.radius), node.width); parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth; } - }, { - key: "_placeInTree", /** * determine in which branch the node will be placed. @@ -34203,6 +34379,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param skipMassUpdate * @private */ + }, { + key: "_placeInTree", value: function _placeInTree(parentBranch, node, skipMassUpdate) { if (skipMassUpdate != true || skipMassUpdate === undefined) { // update the mass of the branch. @@ -34229,8 +34407,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "_placeInRegion", /** * actually place the node in a region (or branch) @@ -34240,6 +34416,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param region * @private */ + }, { + key: "_placeInRegion", value: function _placeInRegion(parentBranch, node, region) { switch (parentBranch.children[region].childrenCount) { case 0: @@ -34266,8 +34444,6 @@ return /******/ (function(modules) { // webpackBootstrap break; } } - }, { - key: "_splitBranch", /** * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch @@ -34276,6 +34452,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentBranch * @private */ + }, { + key: "_splitBranch", value: function _splitBranch(parentBranch) { // if the branch is shaded with a node, replace the node in the new subset. var containedNode = null; @@ -34296,8 +34474,6 @@ return /******/ (function(modules) { // webpackBootstrap this._placeInTree(parentBranch, containedNode); } } - }, { - key: "_insertRegion", /** * This function subdivides the region into four new segments. @@ -34309,6 +34485,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentRange * @private */ + }, { + key: "_insertRegion", value: function _insertRegion(parentBranch, region) { var minX = undefined, maxX = undefined, @@ -34354,8 +34532,6 @@ return /******/ (function(modules) { // webpackBootstrap childrenCount: 0 }; } - }, { - key: "_debug", //--------------------------- DEBUGGING BELOW ---------------------------// @@ -34366,6 +34542,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param color * @private */ + }, { + key: "_debug", value: function _debug(ctx, color) { if (this.barnesHutTree !== undefined) { @@ -34374,8 +34552,6 @@ return /******/ (function(modules) { // webpackBootstrap this._drawBranch(this.barnesHutTree.root, ctx, color); } } - }, { - key: "_drawBranch", /** * This function is for debugging purposes. It draws the branches recursively. @@ -34385,6 +34561,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param color * @private */ + }, { + key: "_drawBranch", value: function _drawBranch(branch, ctx, color) { if (color === undefined) { color = "#FF0000"; @@ -34460,8 +34638,6 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { this.options = options; } - }, { - key: "solve", /** * Calculate the forces the nodes apply on each other based on a repulsion field. @@ -34469,6 +34645,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: "solve", value: function solve() { var dx, dy, distance, fx, fy, repulsingForce, node1, node2; @@ -34555,8 +34733,6 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { this.options = options; } - }, { - key: "solve", /** * Calculate the forces the nodes apply on each other based on a repulsion field. @@ -34564,6 +34740,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: "solve", value: function solve() { var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j; @@ -34646,19 +34824,20 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { this.options = options; } - }, { - key: "solve", /** * This function calculates the springforces on the nodes, accounting for the support nodes. * * @private */ + }, { + key: "solve", value: function solve() { var edgeLength = undefined, edge = undefined; var edgeIndices = this.physicsBody.physicsEdgeIndices; var edges = this.body.edges; + var nodes = this.body.nodes; var node1 = undefined, node2 = undefined, node3 = undefined; @@ -34671,9 +34850,9 @@ return /******/ (function(modules) { // webpackBootstrap if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) { if (edge.edgeType.via !== undefined) { edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; - node1 = edge.to; - node2 = edge.edgeType.via; - node3 = edge.from; + node1 = nodes[edge.to.id]; + node2 = nodes[edge.edgeType.via.id]; + node3 = nodes[edge.from.id]; this._calculateSpringForce(node1, node2, 0.5 * edgeLength); this._calculateSpringForce(node2, node3, 0.5 * edgeLength); @@ -34681,14 +34860,12 @@ return /******/ (function(modules) { // webpackBootstrap // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger. edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5 : edge.options.length; - this._calculateSpringForce(edge.from, edge.to, edgeLength); + this._calculateSpringForce(nodes[edge.from.id], nodes[edge.to.id], edgeLength); } } } } } - }, { - key: "_calculateSpringForce", /** * This is the code actually performing the calculation for the function above. @@ -34698,6 +34875,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param edgeLength * @private */ + }, { + key: "_calculateSpringForce", value: function _calculateSpringForce(node1, node2, edgeLength) { var dx = node1.x - node2.x; var dy = node1.y - node2.y; @@ -34756,14 +34935,14 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { this.options = options; } - }, { - key: "solve", /** * This function calculates the springforces on the nodes, accounting for the support nodes. * * @private */ + }, { + key: "solve", value: function solve() { var edgeLength, edge; var dx, dy, fx, fy, springForce, distance; @@ -34906,13 +35085,13 @@ return /******/ (function(modules) { // webpackBootstrap this._calculateForces(distance, dx, dy, forces, node); } } - }, { - key: "_calculateForces", /** * Calculate the forces based on the distance. * @private */ + }, { + key: "_calculateForces", value: function _calculateForces(distance, dx, dy, forces, node) { var gravityForce = distance === 0 ? 0 : this.options.centralGravity / distance; forces[node.id].x = dx * gravityForce; @@ -34944,7 +35123,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _BarnesHutSolver2 = __webpack_require__(91); @@ -34959,19 +35138,19 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); } + /** + * Calculate the forces based on the distance. + * + * @param distance + * @param dx + * @param dy + * @param node + * @param parentBranch + * @private + */ + _createClass(ForceAtlas2BasedRepulsionSolver, [{ key: "_calculateForces", - - /** - * Calculate the forces based on the distance. - * - * @param distance - * @param dx - * @param dy - * @param node - * @param parentBranch - * @private - */ value: function _calculateForces(distance, dx, dy, node, parentBranch) { if (distance === 0) { distance = 0.1 * Math.random(); @@ -35018,7 +35197,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _CentralGravitySolver2 = __webpack_require__(96); @@ -35033,13 +35212,13 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); } + /** + * Calculate the forces based on the distance. + * @private + */ + _createClass(ForceAtlas2BasedCentralGravitySolver, [{ key: "_calculateForces", - - /** - * Calculate the forces based on the distance. - * @private - */ value: function _calculateForces(distance, dx, dy, forces, node) { if (distance > 0) { var degree = node.edges.length + 1; @@ -35058,6 +35237,14 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, /* 99 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = function() { + return new Worker(__webpack_require__.p + "vis.physics.worker.js"); + }; + +/***/ }, +/* 100 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -35072,7 +35259,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsNodesCluster = __webpack_require__(100); + var _componentsNodesCluster = __webpack_require__(101); var _componentsNodesCluster2 = _interopRequireDefault(_componentsNodesCluster); @@ -35101,18 +35288,18 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { if (options !== undefined) {} } - }, { - key: 'clusterByHubsize', /** * * @param hubsize * @param options */ + }, { + key: 'clusterByHubsize', value: function clusterByHubsize(hubsize, options) { if (hubsize === undefined) { hubsize = this._getHubSize(); - } else if (typeof hubsize === 'object') { + } else if (typeof hubsize === "object") { options = this._checkOptions(hubsize); hubsize = this._getHubSize(); } @@ -35131,20 +35318,20 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_dataChanged'); } - }, { - key: 'cluster', /** * loop over all nodes, check if they adhere to the condition and cluster if needed. * @param options * @param refreshData */ + }, { + key: 'cluster', value: function cluster() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var refreshData = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; if (options.joinCondition === undefined) { - throw new Error('Cannot call clusterByNodeData without a joinCondition function in the options.'); + throw new Error("Cannot call clusterByNodeData without a joinCondition function in the options."); } // check if the options object is fine, append if needed @@ -35173,8 +35360,6 @@ return /******/ (function(modules) { // webpackBootstrap this._cluster(childNodesObj, childEdgesObj, options, refreshData); } - }, { - key: 'clusterByEdgeCount', /** * Cluster all nodes in the network that have only X edges @@ -35182,6 +35367,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param options * @param refreshData */ + }, { + key: 'clusterByEdgeCount', value: function clusterByEdgeCount(edgeCount, options) { var refreshData = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2]; @@ -35256,34 +35443,32 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_dataChanged'); } } - }, { - key: 'clusterOutliers', /** * Cluster all nodes in the network that have only 1 edge * @param options * @param refreshData */ + }, { + key: 'clusterOutliers', value: function clusterOutliers(options) { var refreshData = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; this.clusterByEdgeCount(1, options, refreshData); } - }, { - key: 'clusterBridges', /** * Cluster all nodes in the network that have only 2 edge * @param options * @param refreshData */ + }, { + key: 'clusterBridges', value: function clusterBridges(options) { var refreshData = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; this.clusterByEdgeCount(2, options, refreshData); } - }, { - key: 'clusterByConnection', /** * suck all connected nodes of a node into the node. @@ -35291,15 +35476,17 @@ return /******/ (function(modules) { // webpackBootstrap * @param options * @param refreshData */ + }, { + key: 'clusterByConnection', value: function clusterByConnection(nodeId, options) { var refreshData = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2]; // kill conditions if (nodeId === undefined) { - throw new Error('No nodeId supplied to clusterByConnection!'); + throw new Error("No nodeId supplied to clusterByConnection!"); } if (this.body.nodes[nodeId] === undefined) { - throw new Error('The nodeId given to clusterByConnection does not exist!'); + throw new Error("The nodeId given to clusterByConnection does not exist!"); } var node = this.body.nodes[nodeId]; @@ -35352,8 +35539,6 @@ return /******/ (function(modules) { // webpackBootstrap this._cluster(childNodesObj, childEdgesObj, options, refreshData); } - }, { - key: '_cloneOptions', /** * This returns a clone of the options or options of the edge or node to be used for construction of new edges or check functions for new nodes. @@ -35362,6 +35547,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{}} * @private */ + }, { + key: '_cloneOptions', value: function _cloneOptions(item, type) { var clonedOptions = {}; if (type === undefined || type === 'node') { @@ -35374,8 +35561,6 @@ return /******/ (function(modules) { // webpackBootstrap } return clonedOptions; } - }, { - key: '_createClusterEdges', /** * This function creates the edges that will be attached to the cluster @@ -35386,6 +35571,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param options * @private */ + }, { + key: '_createClusterEdges', value: function _createClusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, clusterEdgeProperties) { var edge = undefined, childNodeId = undefined, @@ -35460,8 +35647,6 @@ return /******/ (function(modules) { // webpackBootstrap _edge.hiddenByCluster = true; } } - }, { - key: '_checkOptions', /** * This function checks the options that can be supplied to the different cluster functions @@ -35470,6 +35655,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ + }, { + key: '_checkOptions', value: function _checkOptions() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; @@ -35482,8 +35669,6 @@ return /******/ (function(modules) { // webpackBootstrap return options; } - }, { - key: '_cluster', /** * @@ -35493,6 +35678,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Boolean} refreshData | when true, do not wrap up * @private */ + }, { + key: '_cluster', value: function _cluster(childNodesObj, childEdgesObj, options) { var refreshData = arguments.length <= 3 || arguments[3] === undefined ? true : arguments[3]; @@ -35528,7 +35715,7 @@ return /******/ (function(modules) { // webpackBootstrap for (var edgeId in childEdgesObj) { if (childEdgesObj.hasOwnProperty(edgeId)) { // these cluster edges will be removed on creation of the cluster. - if (edgeId.substr(0, 12) !== 'clusterEdge:') { + if (edgeId.substr(0, 12) !== "clusterEdge:") { var clonedOptions = this._cloneOptions(childEdgesObj[edgeId], 'edge'); childEdgesOptions.push(clonedOptions); } @@ -35537,7 +35724,7 @@ return /******/ (function(modules) { // webpackBootstrap clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions); if (!clusterNodeProperties) { - throw new Error('The processProperties function does not return properties!'); + throw new Error("The processProperties function does not return properties!"); } } @@ -35608,24 +35795,22 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_dataChanged'); } } - }, { - key: 'isCluster', /** * Check if a node is a cluster. * @param nodeId * @returns {*} */ + }, { + key: 'isCluster', value: function isCluster(nodeId) { if (this.body.nodes[nodeId] !== undefined) { return this.body.nodes[nodeId].isCluster === true; } else { - console.log('Node does not exist.'); + console.log("Node does not exist."); return false; } } - }, { - key: '_getClusterPosition', /** * get the position of the cluster node based on what's inside @@ -35633,6 +35818,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ + }, { + key: '_getClusterPosition', value: function _getClusterPosition(childNodesObj) { var childKeys = Object.keys(childNodesObj); var minX = childNodesObj[childKeys[0]].x; @@ -35650,26 +35837,26 @@ return /******/ (function(modules) { // webpackBootstrap return { x: 0.5 * (minX + maxX), y: 0.5 * (minY + maxY) }; } - }, { - key: 'openCluster', /** * Open a cluster by calling this function. * @param {String} clusterNodeId | the ID of the cluster node * @param {Boolean} refreshData | wrap up afterwards if not true */ + }, { + key: 'openCluster', value: function openCluster(clusterNodeId, options) { var refreshData = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2]; // kill conditions if (clusterNodeId === undefined) { - throw new Error('No clusterNodeId supplied to openCluster.'); + throw new Error("No clusterNodeId supplied to openCluster."); } if (this.body.nodes[clusterNodeId] === undefined) { - throw new Error('The clusterNodeId supplied to openCluster does not exist.'); + throw new Error("The clusterNodeId supplied to openCluster does not exist."); } if (this.body.nodes[clusterNodeId].containedNodes === undefined) { - console.log('The node:' + clusterNodeId + ' is not a cluster.'); + console.log("The node:" + clusterNodeId + " is not a cluster."); return; } var clusterNode = this.body.nodes[clusterNodeId]; @@ -35817,8 +36004,6 @@ return /******/ (function(modules) { // webpackBootstrap return nodesArray; } - }, { - key: 'findNode', /** * Get the stack clusterId's that a certain node resides in. cluster A -> cluster B -> cluster C -> node @@ -35826,6 +36011,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {Array} * @private */ + }, { + key: 'findNode', value: function findNode(nodeId) { var stack = []; var max = 100; @@ -35839,8 +36026,6 @@ return /******/ (function(modules) { // webpackBootstrap stack.push(this.body.nodes[nodeId]); return stack; } - }, { - key: '_getConnectedId', /** * Get the Id the node is connected to @@ -35849,6 +36034,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ + }, { + key: '_getConnectedId', value: function _getConnectedId(edge, nodeId) { if (edge.toId != nodeId) { return edge.toId; @@ -35858,8 +36045,6 @@ return /******/ (function(modules) { // webpackBootstrap return edge.fromId; } } - }, { - key: '_getHubSize', /** * We determine how many connections denote an important hub. @@ -35867,6 +36052,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: '_getHubSize', value: function _getHubSize() { var average = 0; var averageSquared = 0; @@ -35906,7 +36093,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 100 */ +/* 101 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -35921,7 +36108,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _Node2 = __webpack_require__(62); @@ -35951,7 +36138,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 101 */ +/* 102 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -36002,33 +36189,33 @@ return /******/ (function(modules) { // webpackBootstrap value: function bindEventListeners() { var _this = this; - this.body.emitter.on('dragStart', function () { + this.body.emitter.on("dragStart", function () { _this.dragging = true; }); - this.body.emitter.on('dragEnd', function () { + this.body.emitter.on("dragEnd", function () { return _this.dragging = false; }); - this.body.emitter.on('_resizeNodes', function () { + this.body.emitter.on("_resizeNodes", function () { return _this._resizeNodes(); }); - this.body.emitter.on('_redraw', function () { + this.body.emitter.on("_redraw", function () { if (_this.renderingActive === false) { _this._redraw(); } }); - this.body.emitter.on('_blockRedraw', function () { + this.body.emitter.on("_blockRedraw", function () { _this.allowRedraw = false; }); - this.body.emitter.on('_allowRedraw', function () { + this.body.emitter.on("_allowRedraw", function () { _this.allowRedraw = true;_this.redrawRequested = false; }); - this.body.emitter.on('_requestRedraw', this._requestRedraw.bind(this)); - this.body.emitter.on('_startRendering', function () { + this.body.emitter.on("_requestRedraw", this._requestRedraw.bind(this)); + this.body.emitter.on("_startRendering", function () { _this.renderRequests += 1; _this.renderingActive = true; _this._startRendering(); }); - this.body.emitter.on('_stopRendering', function () { + this.body.emitter.on("_stopRendering", function () { _this.renderRequests -= 1; _this.renderingActive = _this.renderRequests > 0; _this.renderTimer = undefined; @@ -36061,8 +36248,8 @@ return /******/ (function(modules) { // webpackBootstrap if (this.requiresTimeout === true) { this.renderTimer = window.setTimeout(this._renderStep.bind(this), this.simulationInterval); // wait this.renderTimeStep milliseconds and perform the animation step function } else { - this.renderTimer = window.requestAnimationFrame(this._renderStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function - } + this.renderTimer = window.requestAnimationFrame(this._renderStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function + } } } } @@ -36086,25 +36273,25 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'redraw', /** * Redraw the network with the current data * chart will be resized too. */ + }, { + key: 'redraw', value: function redraw() { this.body.emitter.emit('setSize'); this._redraw(); } - }, { - key: '_requestRedraw', /** * Redraw the network with the current data * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over. * @private */ + }, { + key: '_requestRedraw', value: function _requestRedraw() { var _this2 = this; @@ -36127,7 +36314,7 @@ return /******/ (function(modules) { // webpackBootstrap var hidden = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; if (this.allowRedraw === true) { - this.body.emitter.emit('initRedraw'); + this.body.emitter.emit("initRedraw"); this.redrawRequested = false; var ctx = this.canvas.frame.canvas.getContext('2d'); @@ -36157,7 +36344,7 @@ return /******/ (function(modules) { // webpackBootstrap ctx.scale(this.body.view.scale, this.body.view.scale); ctx.beginPath(); - this.body.emitter.emit('beforeDrawing', ctx); + this.body.emitter.emit("beforeDrawing", ctx); ctx.closePath(); if (hidden === false) { @@ -36176,7 +36363,7 @@ return /******/ (function(modules) { // webpackBootstrap ctx.beginPath(); //this.physics.nodesSolver._debug(ctx,"#F00F0F"); - this.body.emitter.emit('afterDrawing', ctx); + this.body.emitter.emit("afterDrawing", ctx); ctx.closePath(); // restore original scaling and translation ctx.restore(); @@ -36186,8 +36373,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_resizeNodes', /** * Redraw all nodes @@ -36196,6 +36381,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Boolean} [alwaysShow] * @private */ + }, { + key: '_resizeNodes', value: function _resizeNodes() { var ctx = this.canvas.frame.canvas.getContext('2d'); if (this.pixelRatio === undefined) { @@ -36221,8 +36408,6 @@ return /******/ (function(modules) { // webpackBootstrap // restore original scaling and translation ctx.restore(); } - }, { - key: '_drawNodes', /** * Redraw all nodes @@ -36231,6 +36416,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Boolean} [alwaysShow] * @private */ + }, { + key: '_drawNodes', value: function _drawNodes(ctx) { var alwaysShow = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -36269,8 +36456,6 @@ return /******/ (function(modules) { // webpackBootstrap node.draw(ctx); } } - }, { - key: '_drawEdges', /** * Redraw all edges @@ -36278,6 +36463,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {CanvasRenderingContext2D} ctx * @private */ + }, { + key: '_drawEdges', value: function _drawEdges(ctx) { var edges = this.body.edges; var edgeIndices = this.body.edgeIndices; @@ -36290,8 +36477,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_drawControlNodes', /** * Redraw all edges @@ -36299,6 +36484,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {CanvasRenderingContext2D} ctx * @private */ + }, { + key: '_drawControlNodes', value: function _drawControlNodes(ctx) { var edges = this.body.edges; var edgeIndices = this.body.edgeIndices; @@ -36309,14 +36496,14 @@ return /******/ (function(modules) { // webpackBootstrap edge._drawControlNodes(ctx); } } - }, { - key: '_determineBrowserMethod', /** * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because * some implementations (safari and IE9) did not support requestAnimationFrame * @private */ + }, { + key: '_determineBrowserMethod', value: function _determineBrowserMethod() { if (typeof window !== 'undefined') { var browserType = navigator.userAgent.toLowerCase(); @@ -36343,7 +36530,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 102 */ +/* 103 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -36396,7 +36583,7 @@ return /******/ (function(modules) { // webpackBootstrap var _this = this; // bind the events - this.body.emitter.once('resize', function (obj) { + this.body.emitter.once("resize", function (obj) { if (obj.width !== 0) { _this.body.view.translation.x = obj.width * 0.5; } @@ -36404,8 +36591,8 @@ return /******/ (function(modules) { // webpackBootstrap _this.body.view.translation.y = obj.height * 0.5; } }); - this.body.emitter.on('setSize', this.setSize.bind(this)); - this.body.emitter.on('destroy', function () { + this.body.emitter.on("setSize", this.setSize.bind(this)); + this.body.emitter.on("destroy", function () { _this.hammerFrame.destroy(); _this.hammer.destroy(); _this._cleanUp(); @@ -36427,7 +36614,7 @@ return /******/ (function(modules) { // webpackBootstrap this.resizeTimer = setInterval(function () { var changed = _this2.setSize(); if (changed === true) { - _this2.body.emitter.emit('_requestRedraw'); + _this2.body.emitter.emit("_requestRedraw"); } }, 1000); this.resizeFunction = this._onResize.bind(this); @@ -36448,15 +36635,15 @@ return /******/ (function(modules) { // webpackBootstrap key: '_onResize', value: function _onResize() { this.setSize(); - this.body.emitter.emit('_redraw'); + this.body.emitter.emit("_redraw"); } - }, { - key: '_getCameraState', /** * Get and store the cameraState * @private */ + }, { + key: '_getCameraState', value: function _getCameraState() { var pixelRatio = arguments.length <= 0 || arguments[0] === undefined ? this.pixelRatio : arguments[0]; @@ -36465,13 +36652,13 @@ return /******/ (function(modules) { // webpackBootstrap this.cameraState.scale = this.body.view.scale; this.cameraState.position = this.DOMtoCanvas({ x: 0.5 * this.frame.canvas.width / pixelRatio, y: 0.5 * this.frame.canvas.height / pixelRatio }); } - }, { - key: '_setCameraState', /** * Set the cameraState * @private */ + }, { + key: '_setCameraState', value: function _setCameraState() { if (this.cameraState.scale !== undefined && this.frame.canvas.clientWidth !== 0 && this.frame.canvas.clientHeight !== 0 && this.pixelRatio !== 0 && this.cameraState.previousWidth > 0) { @@ -36505,12 +36692,12 @@ return /******/ (function(modules) { // webpackBootstrap } throw new Error('Could not use the value supplied for width or height:' + value); } - }, { - key: '_create', /** * Create the HTML */ + }, { + key: '_create', value: function _create() { // remove all elements from the container element. while (this.body.container.hasChildNodes()) { @@ -36525,7 +36712,7 @@ return /******/ (function(modules) { // webpackBootstrap ////////////////////////////////////////////////////////////////// - this.frame.canvas = document.createElement('canvas'); + this.frame.canvas = document.createElement("canvas"); this.frame.canvas.style.position = 'relative'; this.frame.appendChild(this.frame.canvas); @@ -36537,10 +36724,10 @@ return /******/ (function(modules) { // webpackBootstrap noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; this.frame.canvas.appendChild(noCanvas); } else { - var ctx = this.frame.canvas.getContext('2d'); + var ctx = this.frame.canvas.getContext("2d"); this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); - this.frame.canvas.getContext('2d').setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); } // add the frame to the container element @@ -36551,13 +36738,13 @@ return /******/ (function(modules) { // webpackBootstrap this._bindHammer(); } - }, { - key: '_bindHammer', /** * This function binds hammer, it can be repeated over and over due to the uniqueness check. * @private */ + }, { + key: '_bindHammer', value: function _bindHammer() { var _this3 = this; @@ -36618,8 +36805,6 @@ return /******/ (function(modules) { // webpackBootstrap _this3.body.eventListeners.onRelease(event); }); } - }, { - key: 'setSize', /** * Set a new size for the network @@ -36628,6 +36813,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {string} height Height in pixels or percentage (for example '400px' * or '30%') */ + }, { + key: 'setSize', value: function setSize() { var width = arguments.length <= 0 || arguments[0] === undefined ? this.options.width : arguments[0]; var height = arguments.length <= 1 || arguments[1] === undefined ? this.options.height : arguments[1]; @@ -36640,7 +36827,7 @@ return /******/ (function(modules) { // webpackBootstrap var oldHeight = this.frame.canvas.height; // update the pixelratio - var ctx = this.frame.canvas.getContext('2d'); + var ctx = this.frame.canvas.getContext("2d"); var previousRation = this.pixelRatio; // we cache this because the camera state storage needs the old value this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); @@ -36706,8 +36893,6 @@ return /******/ (function(modules) { // webpackBootstrap value: function _XconvertDOMtoCanvas(x) { return (x - this.body.view.translation.x) / this.body.view.scale; } - }, { - key: '_XconvertCanvasToDOM', /** * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to @@ -36716,11 +36901,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: '_XconvertCanvasToDOM', value: function _XconvertCanvasToDOM(x) { return x * this.body.view.scale + this.body.view.translation.x; } - }, { - key: '_YconvertDOMtoCanvas', /** * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to @@ -36729,11 +36914,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: '_YconvertDOMtoCanvas', value: function _YconvertDOMtoCanvas(y) { return (y - this.body.view.translation.y) / this.body.view.scale; } - }, { - key: '_YconvertCanvasToDOM', /** * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to @@ -36742,11 +36927,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: '_YconvertCanvasToDOM', value: function _YconvertCanvasToDOM(y) { return y * this.body.view.scale + this.body.view.translation.y; } - }, { - key: 'canvasToDOM', /** * @@ -36754,11 +36939,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @constructor */ + }, { + key: 'canvasToDOM', value: function canvasToDOM(pos) { return { x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y) }; } - }, { - key: 'DOMtoCanvas', /** * @@ -36766,6 +36951,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @constructor */ + }, { + key: 'DOMtoCanvas', value: function DOMtoCanvas(pos) { return { x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y) }; } @@ -36778,7 +36965,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 103 */ +/* 104 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -36793,7 +36980,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _NetworkUtil = __webpack_require__(104); + var _NetworkUtil = __webpack_require__(105); var _NetworkUtil2 = _interopRequireDefault(_NetworkUtil); @@ -36809,7 +36996,7 @@ return /******/ (function(modules) { // webpackBootstrap this.canvas = canvas; this.animationSpeed = 1 / this.renderRefreshRate; - this.animationEasingFunction = 'easeInOutQuint'; + this.animationEasingFunction = "easeInOutQuint"; this.easingTime = 0; this.sourceScale = 0; this.targetScale = 0; @@ -36821,11 +37008,11 @@ return /******/ (function(modules) { // webpackBootstrap this.viewFunction = undefined; - this.body.emitter.on('fit', this.fit.bind(this)); - this.body.emitter.on('animationFinished', function () { - _this.body.emitter.emit('_stopRendering'); + this.body.emitter.on("fit", this.fit.bind(this)); + this.body.emitter.on("animationFinished", function () { + _this.body.emitter.emit("_stopRendering"); }); - this.body.emitter.on('unlockNode', this.releaseNode.bind(this)); + this.body.emitter.on("unlockNode", this.releaseNode.bind(this)); } _createClass(View, [{ @@ -36835,14 +37022,14 @@ return /******/ (function(modules) { // webpackBootstrap this.options = options; } - }, { - key: 'fit', /** * This function zooms out to fit all data on screen based on amount of nodes * @param {Object} Options * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; */ + }, { + key: 'fit', value: function fit() { var options = arguments.length <= 0 || arguments[0] === undefined ? { nodes: [] } : arguments[0]; var initialZoom = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -36878,7 +37065,7 @@ return /******/ (function(modules) { // webpackBootstrap var factor = Math.min(this.canvas.frame.canvas.clientWidth / 600, this.canvas.frame.canvas.clientHeight / 600); zoomLevel *= factor; } else { - this.body.emitter.emit('_resizeNodes'); + this.body.emitter.emit("_resizeNodes"); range = _NetworkUtil2['default']._getRange(this.body.nodes, options.nodes); var xDistance = Math.abs(range.maxX - range.minX) * 1.1; @@ -36900,8 +37087,6 @@ return /******/ (function(modules) { // webpackBootstrap var animationOptions = { position: center, scale: zoomLevel, animation: options.animation }; this.moveTo(animationOptions); } - }, { - key: 'focus', // animation @@ -36911,6 +37096,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} nodeId * @param {Number} [options] */ + }, { + key: 'focus', value: function focus(nodeId) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; @@ -36921,11 +37108,9 @@ return /******/ (function(modules) { // webpackBootstrap this.moveTo(options); } else { - console.log('Node: ' + nodeId + ' cannot be found.'); + console.log("Node: " + nodeId + " cannot be found."); } } - }, { - key: 'moveTo', /** * @@ -36934,6 +37119,8 @@ return /******/ (function(modules) { // webpackBootstrap * | options.position = {x:Number, y:Number} // position to move to * | options.animation = {duration:Number, easingFunction:String} || Boolean // position to move to */ + }, { + key: 'moveTo', value: function moveTo(options) { if (options === undefined) { options = {}; @@ -36967,13 +37154,11 @@ return /******/ (function(modules) { // webpackBootstrap options.animation.duration = 1000; } // default duration if (options.animation.easingFunction === undefined) { - options.animation.easingFunction = 'easeInOutQuad'; + options.animation.easingFunction = "easeInOutQuad"; } // default easing function this.animateView(options); } - }, { - key: 'animateView', /** * @@ -36986,6 +37171,8 @@ return /******/ (function(modules) { // webpackBootstrap * // easeInQuart, easeOutQuart, easeInOutQuart, * // easeInQuint, easeOutQuint, easeInOutQuint */ + }, { + key: 'animateView', value: function animateView(options) { if (options === undefined) { return; @@ -37025,28 +37212,28 @@ return /******/ (function(modules) { // webpackBootstrap if (options.animation.duration === 0) { if (this.lockedOnNodeId != undefined) { this.viewFunction = this._lockedRedraw.bind(this); - this.body.emitter.on('initRedraw', this.viewFunction); + this.body.emitter.on("initRedraw", this.viewFunction); } else { this.body.view.scale = this.targetScale; this.body.view.translation = this.targetTranslation; - this.body.emitter.emit('_requestRedraw'); + this.body.emitter.emit("_requestRedraw"); } } else { this.animationSpeed = 1 / (60 * options.animation.duration * 0.001) || 1 / 60; // 60 for 60 seconds, 0.001 for milli's this.animationEasingFunction = options.animation.easingFunction; this.viewFunction = this._transitionRedraw.bind(this); - this.body.emitter.on('initRedraw', this.viewFunction); - this.body.emitter.emit('_startRendering'); + this.body.emitter.on("initRedraw", this.viewFunction); + this.body.emitter.emit("_startRendering"); } } - }, { - key: '_lockedRedraw', /** * used to animate smoothly by hijacking the redraw function. * @private */ + }, { + key: '_lockedRedraw', value: function _lockedRedraw() { var nodePosition = { x: this.body.nodes[this.lockedOnNodeId].x, y: this.body.nodes[this.lockedOnNodeId].y }; var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight }); @@ -37066,19 +37253,19 @@ return /******/ (function(modules) { // webpackBootstrap key: 'releaseNode', value: function releaseNode() { if (this.lockedOnNodeId !== undefined && this.viewFunction !== undefined) { - this.body.emitter.off('initRedraw', this.viewFunction); + this.body.emitter.off("initRedraw", this.viewFunction); this.lockedOnNodeId = undefined; this.lockedOnNodeOffset = undefined; } } - }, { - key: '_transitionRedraw', /** * * @param easingTime * @private */ + }, { + key: '_transitionRedraw', value: function _transitionRedraw() { var finished = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; @@ -37095,13 +37282,13 @@ return /******/ (function(modules) { // webpackBootstrap // cleanup if (this.easingTime >= 1.0) { - this.body.emitter.off('initRedraw', this.viewFunction); + this.body.emitter.off("initRedraw", this.viewFunction); this.easingTime = 0; if (this.lockedOnNodeId != undefined) { this.viewFunction = this._lockedRedraw.bind(this); - this.body.emitter.on('initRedraw', this.viewFunction); + this.body.emitter.on("initRedraw", this.viewFunction); } - this.body.emitter.emit('animationFinished'); + this.body.emitter.emit("animationFinished"); } } }, { @@ -37123,7 +37310,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 104 */ +/* 105 */ /***/ function(module, exports) { "use strict"; @@ -37141,13 +37328,13 @@ return /******/ (function(modules) { // webpackBootstrap _classCallCheck(this, NetworkUtil); } + /** + * Find the center position of the network considering the bounding boxes + * @private + */ + _createClass(NetworkUtil, null, [{ key: "_getRange", - - /** - * Find the center position of the network considering the bounding boxes - * @private - */ value: function _getRange(allNodes) { var specificNodes = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; @@ -37179,13 +37366,13 @@ return /******/ (function(modules) { // webpackBootstrap } return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; } - }, { - key: "_getRangeCore", /** * Find the center position of the network * @private */ + }, { + key: "_getRangeCore", value: function _getRangeCore(allNodes) { var specificNodes = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; @@ -37217,14 +37404,14 @@ return /******/ (function(modules) { // webpackBootstrap } return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; } - }, { - key: "_findCenter", /** * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; * @returns {{x: number, y: number}} * @private */ + }, { + key: "_findCenter", value: function _findCenter(range) { return { x: 0.5 * (range.maxX + range.minX), y: 0.5 * (range.maxY + range.minY) }; @@ -37238,7 +37425,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 105 */ +/* 106 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37253,11 +37440,11 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsNavigationHandler = __webpack_require__(106); + var _componentsNavigationHandler = __webpack_require__(107); var _componentsNavigationHandler2 = _interopRequireDefault(_componentsNavigationHandler); - var _componentsPopup = __webpack_require__(107); + var _componentsPopup = __webpack_require__(108); var _componentsPopup2 = _interopRequireDefault(_componentsPopup); @@ -37345,8 +37532,6 @@ return /******/ (function(modules) { // webpackBootstrap this.navigationHandler.setOptions(this.options); } - }, { - key: 'getPointer', /** * Get the pointer location from a touch location @@ -37354,20 +37539,22 @@ return /******/ (function(modules) { // webpackBootstrap * @return {{x: Number, y: Number}} pointer * @private */ + }, { + key: 'getPointer', value: function getPointer(touch) { return { x: touch.x - util.getAbsoluteLeft(this.canvas.frame.canvas), y: touch.y - util.getAbsoluteTop(this.canvas.frame.canvas) }; } - }, { - key: 'onTouch', /** * On start of a touch gesture, store the pointer * @param event * @private */ + }, { + key: 'onTouch', value: function onTouch(event) { if (new Date().valueOf() - this.touchTime > 50) { this.drag.pointer = this.getPointer(event.center); @@ -37377,13 +37564,13 @@ return /******/ (function(modules) { // webpackBootstrap this.touchTime = new Date().valueOf(); } } - }, { - key: 'onTap', /** * handle tap/click event: select/unselect a node * @private */ + }, { + key: 'onTap', value: function onTap(event) { var pointer = this.getPointer(event.center); var multiselect = this.selectionHandler.options.multiselect && (event.changedPointers[0].ctrlKey || event.changedPointers[0].metaKey); @@ -37391,24 +37578,24 @@ return /******/ (function(modules) { // webpackBootstrap this.checkSelectionChanges(pointer, event, multiselect); this.selectionHandler._generateClickEvent('click', event, pointer); } - }, { - key: 'onDoubleTap', /** * handle doubletap event * @private */ + }, { + key: 'onDoubleTap', value: function onDoubleTap(event) { var pointer = this.getPointer(event.center); this.selectionHandler._generateClickEvent('doubleClick', event, pointer); } - }, { - key: 'onHold', /** * handle long tap event: multi select nodes * @private */ + }, { + key: 'onHold', value: function onHold(event) { var pointer = this.getPointer(event.center); var multiselect = this.selectionHandler.options.multiselect; @@ -37418,14 +37605,14 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionHandler._generateClickEvent('click', event, pointer); this.selectionHandler._generateClickEvent('hold', event, pointer); } - }, { - key: 'onRelease', /** * handle the release of the screen * * @private */ + }, { + key: 'onRelease', value: function onRelease(event) { if (new Date().valueOf() - this.touchTime > 10) { var pointer = this.getPointer(event.center); @@ -37440,14 +37627,14 @@ return /******/ (function(modules) { // webpackBootstrap var pointer = this.getPointer({ x: event.clientX, y: event.clientY }); this.selectionHandler._generateClickEvent('oncontext', event, pointer); } - }, { - key: 'checkSelectionChanges', /** * * @param pointer * @param add */ + }, { + key: 'checkSelectionChanges', value: function checkSelectionChanges(pointer, event) { var add = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; @@ -37508,8 +37695,6 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionHandler._generateClickEvent('select', event, pointer); } } - }, { - key: '_determineIfDifferent', /** * This function checks if the nodes and edges previously selected have changed. @@ -37518,6 +37703,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{nodesChanges: boolean, edgesChanges: boolean}} * @private */ + }, { + key: '_determineIfDifferent', value: function _determineIfDifferent(previousSelection, currentSelection) { var nodesChanges = false; var edgesChanges = false; @@ -37545,8 +37732,6 @@ return /******/ (function(modules) { // webpackBootstrap return { nodesChanges: nodesChanges, edgesChanges: edgesChanges }; } - }, { - key: 'onDragStart', /** * This function is called by onDragStart. @@ -37554,6 +37739,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: 'onDragStart', value: function onDragStart(event) { //in case the touch event was triggered on an external div, do the initial touch now. if (this.drag.pointer === undefined) { @@ -37606,13 +37793,13 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionHandler._generateClickEvent('dragStart', event, this.drag.pointer, undefined, true); } } - }, { - key: 'onDrag', /** * handle drag event * @private */ + }, { + key: 'onDrag', value: function onDrag(event) { var _this2 = this; @@ -37668,13 +37855,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'onDragEnd', /** * handle drag start event * @private */ + }, { + key: 'onDragEnd', value: function onDragEnd(event) { this.drag.dragging = false; var selection = this.drag.selection; @@ -37691,14 +37878,14 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_requestRedraw'); } } - }, { - key: 'onPinch', /** * Handle pinch event * @param event * @private */ + }, { + key: 'onPinch', value: function onPinch(event) { var pointer = this.getPointer(event.center); @@ -37711,8 +37898,6 @@ return /******/ (function(modules) { // webpackBootstrap var scale = this.pinch.scale * event.scale; this.zoom(scale, pointer); } - }, { - key: 'zoom', /** * Zoom the network in or out @@ -37721,6 +37906,8 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number} appliedScale scale is limited within the boundaries * @private */ + }, { + key: 'zoom', value: function zoom(scale, pointer) { if (this.options.zoomView === true) { var scaleOld = this.body.view.scale; @@ -37762,8 +37949,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'onMouseWheel', /** * Event handler for mouse wheel event, used to zoom the timeline @@ -37772,6 +37957,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {MouseEvent} event * @private */ + }, { + key: 'onMouseWheel', value: function onMouseWheel(event) { // retrieve delta var delta = 0; @@ -37808,14 +37995,14 @@ return /******/ (function(modules) { // webpackBootstrap // Prevent default actions caused by mouse wheel. event.preventDefault(); } - }, { - key: 'onMouseMove', /** * Mouse move handler for checking whether the title moves over a node with a title. * @param {Event} event * @private */ + }, { + key: 'onMouseMove', value: function onMouseMove(event) { var _this3 = this; @@ -37866,8 +38053,6 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionHandler.hoverObject(obj); } } - }, { - key: '_checkShowPopup', /** * Check if there is an element on the given position in the network @@ -37877,6 +38062,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {{x:Number, y:Number}} pointer * @private */ + }, { + key: '_checkShowPopup', value: function _checkShowPopup(pointer) { var x = this.canvas._XconvertDOMtoCanvas(pointer.x); var y = this.canvas._YconvertDOMtoCanvas(pointer.y); @@ -37961,8 +38148,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_checkHidePopup', /** * Check if the popup must be hidden, which is the case when the mouse is no @@ -37970,6 +38155,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {{x:Number, y:Number}} pointer * @private */ + }, { + key: '_checkHidePopup', value: function _checkHidePopup(pointer) { var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); @@ -38008,7 +38195,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 106 */ +/* 107 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38041,13 +38228,13 @@ return /******/ (function(modules) { // webpackBootstrap this.touchTime = 0; this.activated = false; - this.body.emitter.on('activate', function () { + this.body.emitter.on("activate", function () { _this.activated = true;_this.configureKeyboardBindings(); }); - this.body.emitter.on('deactivate', function () { + this.body.emitter.on("deactivate", function () { _this.activated = false;_this.configureKeyboardBindings(); }); - this.body.emitter.on('destroy', function () { + this.body.emitter.on("destroy", function () { if (_this.keycharm !== undefined) { _this.keycharm.destroy(); } @@ -38095,8 +38282,6 @@ return /******/ (function(modules) { // webpackBootstrap this.iconsCreated = false; } - }, { - key: 'loadNavigationElements', /** * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation @@ -38106,6 +38291,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: 'loadNavigationElements', value: function loadNavigationElements() { var _this2 = this; @@ -38125,7 +38312,7 @@ return /******/ (function(modules) { // webpackBootstrap this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]); var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); - if (navigationDivActions[i] === '_fit') { + if (navigationDivActions[i] === "_fit") { hammerUtil.onTouch(hammer, this._fit.bind(this)); } else { hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i])); @@ -38149,47 +38336,47 @@ return /******/ (function(modules) { // webpackBootstrap value: function bindToRedraw(action) { if (this.boundFunctions[action] === undefined) { this.boundFunctions[action] = this[action].bind(this); - this.body.emitter.on('initRedraw', this.boundFunctions[action]); - this.body.emitter.emit('_startRendering'); + this.body.emitter.on("initRedraw", this.boundFunctions[action]); + this.body.emitter.emit("_startRendering"); } } }, { key: 'unbindFromRedraw', value: function unbindFromRedraw(action) { if (this.boundFunctions[action] !== undefined) { - this.body.emitter.off('initRedraw', this.boundFunctions[action]); - this.body.emitter.emit('_stopRendering'); + this.body.emitter.off("initRedraw", this.boundFunctions[action]); + this.body.emitter.emit("_stopRendering"); delete this.boundFunctions[action]; } } - }, { - key: '_fit', /** * this stops all movement induced by the navigation buttons * * @private */ + }, { + key: '_fit', value: function _fit() { if (new Date().valueOf() - this.touchTime > 700) { // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?) - this.body.emitter.emit('fit', { duration: 700 }); + this.body.emitter.emit("fit", { duration: 700 }); this.touchTime = new Date().valueOf(); } } - }, { - key: '_stopMovement', /** * this stops all movement induced by the navigation buttons * * @private */ + }, { + key: '_stopMovement', value: function _stopMovement() { for (var boundAction in this.boundFunctions) { if (this.boundFunctions.hasOwnProperty(boundAction)) { - this.body.emitter.off('initRedraw', this.boundFunctions[boundAction]); - this.body.emitter.emit('_stopRendering'); + this.body.emitter.off("initRedraw", this.boundFunctions[boundAction]); + this.body.emitter.emit("_stopRendering"); } } this.boundFunctions = {}; @@ -38226,12 +38413,12 @@ return /******/ (function(modules) { // webpackBootstrap this.body.view.scale /= 1 + this.options.keyboard.speed.zoom; this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale }); } - }, { - key: 'configureKeyboardBindings', /** * bind all keys using keycharm. */ + }, { + key: 'configureKeyboardBindings', value: function configureKeyboardBindings() { var _this3 = this; @@ -38249,79 +38436,79 @@ return /******/ (function(modules) { // webpackBootstrap this.keycharm.reset(); if (this.activated === true) { - this.keycharm.bind('up', function () { - _this3.bindToRedraw('_moveUp'); - }, 'keydown'); - this.keycharm.bind('down', function () { - _this3.bindToRedraw('_moveDown'); - }, 'keydown'); - this.keycharm.bind('left', function () { - _this3.bindToRedraw('_moveLeft'); - }, 'keydown'); - this.keycharm.bind('right', function () { - _this3.bindToRedraw('_moveRight'); - }, 'keydown'); - this.keycharm.bind('=', function () { - _this3.bindToRedraw('_zoomIn'); - }, 'keydown'); - this.keycharm.bind('num+', function () { - _this3.bindToRedraw('_zoomIn'); - }, 'keydown'); - this.keycharm.bind('num-', function () { - _this3.bindToRedraw('_zoomOut'); - }, 'keydown'); - this.keycharm.bind('-', function () { - _this3.bindToRedraw('_zoomOut'); - }, 'keydown'); - this.keycharm.bind('[', function () { - _this3.bindToRedraw('_zoomOut'); - }, 'keydown'); - this.keycharm.bind(']', function () { - _this3.bindToRedraw('_zoomIn'); - }, 'keydown'); - this.keycharm.bind('pageup', function () { - _this3.bindToRedraw('_zoomIn'); - }, 'keydown'); - this.keycharm.bind('pagedown', function () { - _this3.bindToRedraw('_zoomOut'); - }, 'keydown'); - - this.keycharm.bind('up', function () { - _this3.unbindFromRedraw('_moveUp'); - }, 'keyup'); - this.keycharm.bind('down', function () { - _this3.unbindFromRedraw('_moveDown'); - }, 'keyup'); - this.keycharm.bind('left', function () { - _this3.unbindFromRedraw('_moveLeft'); - }, 'keyup'); - this.keycharm.bind('right', function () { - _this3.unbindFromRedraw('_moveRight'); - }, 'keyup'); - this.keycharm.bind('=', function () { - _this3.unbindFromRedraw('_zoomIn'); - }, 'keyup'); - this.keycharm.bind('num+', function () { - _this3.unbindFromRedraw('_zoomIn'); - }, 'keyup'); - this.keycharm.bind('num-', function () { - _this3.unbindFromRedraw('_zoomOut'); - }, 'keyup'); - this.keycharm.bind('-', function () { - _this3.unbindFromRedraw('_zoomOut'); - }, 'keyup'); - this.keycharm.bind('[', function () { - _this3.unbindFromRedraw('_zoomOut'); - }, 'keyup'); - this.keycharm.bind(']', function () { - _this3.unbindFromRedraw('_zoomIn'); - }, 'keyup'); - this.keycharm.bind('pageup', function () { - _this3.unbindFromRedraw('_zoomIn'); - }, 'keyup'); - this.keycharm.bind('pagedown', function () { - _this3.unbindFromRedraw('_zoomOut'); - }, 'keyup'); + this.keycharm.bind("up", function () { + _this3.bindToRedraw("_moveUp"); + }, "keydown"); + this.keycharm.bind("down", function () { + _this3.bindToRedraw("_moveDown"); + }, "keydown"); + this.keycharm.bind("left", function () { + _this3.bindToRedraw("_moveLeft"); + }, "keydown"); + this.keycharm.bind("right", function () { + _this3.bindToRedraw("_moveRight"); + }, "keydown"); + this.keycharm.bind("=", function () { + _this3.bindToRedraw("_zoomIn"); + }, "keydown"); + this.keycharm.bind("num+", function () { + _this3.bindToRedraw("_zoomIn"); + }, "keydown"); + this.keycharm.bind("num-", function () { + _this3.bindToRedraw("_zoomOut"); + }, "keydown"); + this.keycharm.bind("-", function () { + _this3.bindToRedraw("_zoomOut"); + }, "keydown"); + this.keycharm.bind("[", function () { + _this3.bindToRedraw("_zoomOut"); + }, "keydown"); + this.keycharm.bind("]", function () { + _this3.bindToRedraw("_zoomIn"); + }, "keydown"); + this.keycharm.bind("pageup", function () { + _this3.bindToRedraw("_zoomIn"); + }, "keydown"); + this.keycharm.bind("pagedown", function () { + _this3.bindToRedraw("_zoomOut"); + }, "keydown"); + + this.keycharm.bind("up", function () { + _this3.unbindFromRedraw("_moveUp"); + }, "keyup"); + this.keycharm.bind("down", function () { + _this3.unbindFromRedraw("_moveDown"); + }, "keyup"); + this.keycharm.bind("left", function () { + _this3.unbindFromRedraw("_moveLeft"); + }, "keyup"); + this.keycharm.bind("right", function () { + _this3.unbindFromRedraw("_moveRight"); + }, "keyup"); + this.keycharm.bind("=", function () { + _this3.unbindFromRedraw("_zoomIn"); + }, "keyup"); + this.keycharm.bind("num+", function () { + _this3.unbindFromRedraw("_zoomIn"); + }, "keyup"); + this.keycharm.bind("num-", function () { + _this3.unbindFromRedraw("_zoomOut"); + }, "keyup"); + this.keycharm.bind("-", function () { + _this3.unbindFromRedraw("_zoomOut"); + }, "keyup"); + this.keycharm.bind("[", function () { + _this3.unbindFromRedraw("_zoomOut"); + }, "keyup"); + this.keycharm.bind("]", function () { + _this3.unbindFromRedraw("_zoomIn"); + }, "keyup"); + this.keycharm.bind("pageup", function () { + _this3.unbindFromRedraw("_zoomIn"); + }, "keyup"); + this.keycharm.bind("pagedown", function () { + _this3.unbindFromRedraw("_zoomOut"); + }, "keyup"); } } } @@ -38334,7 +38521,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 107 */ +/* 108 */ /***/ function(module, exports) { /** @@ -38373,24 +38560,24 @@ return /******/ (function(modules) { // webpackBootstrap this.container.appendChild(this.frame); } + /** + * @param {number} x Horizontal position of the popup window + * @param {number} y Vertical position of the popup window + */ + _createClass(Popup, [{ key: 'setPosition', - - /** - * @param {number} x Horizontal position of the popup window - * @param {number} y Vertical position of the popup window - */ value: function setPosition(x, y) { this.x = parseInt(x); this.y = parseInt(y); } - }, { - key: 'setText', /** * Set the content for the popup window. This can be HTML code or text. * @param {string | Element} content */ + }, { + key: 'setText', value: function setText(content) { if (content instanceof Element) { this.frame.innerHTML = ''; @@ -38399,13 +38586,13 @@ return /******/ (function(modules) { // webpackBootstrap this.frame.innerHTML = content; // string containing text or HTML } } - }, { - key: 'show', /** * Show the popup window * @param {boolean} [doShow] Show or hide the window */ + }, { + key: 'show', value: function show(doShow) { if (doShow === undefined) { doShow = true; @@ -38433,23 +38620,23 @@ return /******/ (function(modules) { // webpackBootstrap left = this.padding; } - this.frame.style.left = left + 'px'; - this.frame.style.top = top + 'px'; - this.frame.style.visibility = 'visible'; + this.frame.style.left = left + "px"; + this.frame.style.top = top + "px"; + this.frame.style.visibility = "visible"; this.hidden = false; } else { this.hide(); } } - }, { - key: 'hide', /** * Hide the popup window */ + }, { + key: 'hide', value: function hide() { this.hidden = true; - this.frame.style.visibility = 'hidden'; + this.frame.style.visibility = "hidden"; } }]); @@ -38460,7 +38647,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 108 */ +/* 109 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -38506,12 +38693,10 @@ return /******/ (function(modules) { // webpackBootstrap key: "setOptions", value: function setOptions(options) { if (options !== undefined) { - var fields = ["multiselect", "hoverConnectedEdges", "selectable", "selectConnectedEdges"]; + var fields = ['multiselect', 'hoverConnectedEdges', 'selectable', 'selectConnectedEdges']; util.selectiveDeepExtend(fields, this.options, options); } } - }, { - key: "selectOnPoint", /** * handles the selection part of the tap; @@ -38519,6 +38704,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Object} pointer * @private */ + }, { + key: "selectOnPoint", value: function selectOnPoint(pointer) { var selected = false; if (this.options.selectable === true) { @@ -38565,14 +38752,14 @@ return /******/ (function(modules) { // webpackBootstrap } else { properties = this.getSelection(); } - properties["pointer"] = { + properties['pointer'] = { DOM: { x: pointer.x, y: pointer.y }, canvas: this.canvas.DOMtoCanvas(pointer) }; - properties["event"] = event; + properties['event'] = event; if (oldSelection !== undefined) { - properties["previousSelection"] = oldSelection; + properties['previousSelection'] = oldSelection; } this.body.emitter.emit(eventType, properties); } @@ -38601,8 +38788,6 @@ return /******/ (function(modules) { // webpackBootstrap this._removeFromSelection(obj); } } - }, { - key: "_getAllNodesOverlappingWith", /** * retrieve all nodes overlapping with given object @@ -38610,6 +38795,8 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number[]} An array with id's of the overlapping nodes * @private */ + }, { + key: "_getAllNodesOverlappingWith", value: function _getAllNodesOverlappingWith(object) { var overlappingNodes = []; var nodes = this.body.nodes; @@ -38621,8 +38808,6 @@ return /******/ (function(modules) { // webpackBootstrap } return overlappingNodes; } - }, { - key: "_pointerToPositionObject", /** * Return a position object in canvasspace from a single point in screenspace @@ -38631,6 +38816,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{left: number, top: number, right: number, bottom: number}} * @private */ + }, { + key: "_pointerToPositionObject", value: function _pointerToPositionObject(pointer) { var canvasPos = this.canvas.DOMtoCanvas(pointer); return { @@ -38640,8 +38827,6 @@ return /******/ (function(modules) { // webpackBootstrap bottom: canvasPos.y - 1 }; } - }, { - key: "getNodeAt", /** * Get the top node at the a specific point (like a click) @@ -38650,6 +38835,8 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Node | undefined} node * @private */ + }, { + key: "getNodeAt", value: function getNodeAt(pointer) { var returnNode = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; @@ -38668,8 +38855,6 @@ return /******/ (function(modules) { // webpackBootstrap return undefined; } } - }, { - key: "_getEdgesOverlappingWith", /** * retrieve all edges overlapping with given object, selector is around center @@ -38677,6 +38862,8 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number[]} An array with id's of the overlapping nodes * @private */ + }, { + key: "_getEdgesOverlappingWith", value: function _getEdgesOverlappingWith(object, overlappingEdges) { var edges = this.body.edges; for (var i = 0; i < this.body.edgeIndices.length; i++) { @@ -38686,8 +38873,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "_getAllEdgesOverlappingWith", /** * retrieve all nodes overlapping with given object @@ -38695,13 +38880,13 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number[]} An array with id's of the overlapping nodes * @private */ + }, { + key: "_getAllEdgesOverlappingWith", value: function _getAllEdgesOverlappingWith(object) { var overlappingEdges = []; this._getEdgesOverlappingWith(object, overlappingEdges); return overlappingEdges; } - }, { - key: "getEdgeAt", /** * Place holder. To implement change the getNodeAt to a _getObjectAt. Have the _getObjectAt call @@ -38711,6 +38896,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {undefined} * @private */ + }, { + key: "getEdgeAt", value: function getEdgeAt(pointer) { var returnEdge = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; @@ -38727,8 +38914,6 @@ return /******/ (function(modules) { // webpackBootstrap return undefined; } } - }, { - key: "_addToSelection", /** * Add object to the selection array. @@ -38736,6 +38921,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param obj * @private */ + }, { + key: "_addToSelection", value: function _addToSelection(obj) { if (obj instanceof Node) { this.selectionObj.nodes[obj.id] = obj; @@ -38743,8 +38930,6 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionObj.edges[obj.id] = obj; } } - }, { - key: "_addToHover", /** * Add object to the selection array. @@ -38752,6 +38937,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param obj * @private */ + }, { + key: "_addToHover", value: function _addToHover(obj) { if (obj instanceof Node) { this.hoverObj.nodes[obj.id] = obj; @@ -38759,8 +38946,6 @@ return /******/ (function(modules) { // webpackBootstrap this.hoverObj.edges[obj.id] = obj; } } - }, { - key: "_removeFromSelection", /** * Remove a single option from selection. @@ -38768,6 +38953,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Object} obj * @private */ + }, { + key: "_removeFromSelection", value: function _removeFromSelection(obj) { if (obj instanceof Node) { delete this.selectionObj.nodes[obj.id]; @@ -38775,14 +38962,14 @@ return /******/ (function(modules) { // webpackBootstrap delete this.selectionObj.edges[obj.id]; } } - }, { - key: "unselectAll", /** * Unselect all. The selectionObj is useful for this. * * @private */ + }, { + key: "unselectAll", value: function unselectAll() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -38797,8 +38984,6 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionObj = { nodes: {}, edges: {} }; } - }, { - key: "_getSelectedNodeCount", /** * return the number of selected nodes @@ -38806,6 +38991,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: "_getSelectedNodeCount", value: function _getSelectedNodeCount() { var count = 0; for (var nodeId in this.selectionObj.nodes) { @@ -38815,8 +39002,6 @@ return /******/ (function(modules) { // webpackBootstrap } return count; } - }, { - key: "_getSelectedNode", /** * return the selected node @@ -38824,6 +39009,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: "_getSelectedNode", value: function _getSelectedNode() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -38832,8 +39019,6 @@ return /******/ (function(modules) { // webpackBootstrap } return undefined; } - }, { - key: "_getSelectedEdge", /** * return the selected edge @@ -38841,6 +39026,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: "_getSelectedEdge", value: function _getSelectedEdge() { for (var edgeId in this.selectionObj.edges) { if (this.selectionObj.edges.hasOwnProperty(edgeId)) { @@ -38849,8 +39036,6 @@ return /******/ (function(modules) { // webpackBootstrap } return undefined; } - }, { - key: "_getSelectedEdgeCount", /** * return the number of selected edges @@ -38858,6 +39043,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: "_getSelectedEdgeCount", value: function _getSelectedEdgeCount() { var count = 0; for (var edgeId in this.selectionObj.edges) { @@ -38867,8 +39054,6 @@ return /******/ (function(modules) { // webpackBootstrap } return count; } - }, { - key: "_getSelectedObjectCount", /** * return the number of selected objects. @@ -38876,6 +39061,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: "_getSelectedObjectCount", value: function _getSelectedObjectCount() { var count = 0; for (var nodeId in this.selectionObj.nodes) { @@ -38890,8 +39077,6 @@ return /******/ (function(modules) { // webpackBootstrap } return count; } - }, { - key: "_selectionIsEmpty", /** * Check if anything is selected @@ -38899,6 +39084,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {boolean} * @private */ + }, { + key: "_selectionIsEmpty", value: function _selectionIsEmpty() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -38912,8 +39099,6 @@ return /******/ (function(modules) { // webpackBootstrap } return true; } - }, { - key: "_clusterInSelection", /** * check if one of the selected nodes is a cluster. @@ -38921,6 +39106,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {boolean} * @private */ + }, { + key: "_clusterInSelection", value: function _clusterInSelection() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -38931,8 +39118,6 @@ return /******/ (function(modules) { // webpackBootstrap } return false; } - }, { - key: "_selectConnectedEdges", /** * select the edges connected to the node that is being selected @@ -38940,6 +39125,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node} node * @private */ + }, { + key: "_selectConnectedEdges", value: function _selectConnectedEdges(node) { for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; @@ -38947,8 +39134,6 @@ return /******/ (function(modules) { // webpackBootstrap this._addToSelection(edge); } } - }, { - key: "_hoverConnectedEdges", /** * select the edges connected to the node that is being selected @@ -38956,6 +39141,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node} node * @private */ + }, { + key: "_hoverConnectedEdges", value: function _hoverConnectedEdges(node) { for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; @@ -38963,8 +39150,6 @@ return /******/ (function(modules) { // webpackBootstrap this._addToHover(edge); } } - }, { - key: "_unselectConnectedEdges", /** * unselect the edges connected to the node that is being selected @@ -38972,6 +39157,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node} node * @private */ + }, { + key: "_unselectConnectedEdges", value: function _unselectConnectedEdges(node) { for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; @@ -38979,8 +39166,6 @@ return /******/ (function(modules) { // webpackBootstrap this._removeFromSelection(edge); } } - }, { - key: "blurObject", /** * This is called when someone clicks on a node. either select or deselect it. @@ -38989,6 +39174,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node || Edge} object * @private */ + }, { + key: "blurObject", value: function blurObject(object) { if (object.hover === true) { object.hover = false; @@ -38999,8 +39186,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "hoverObject", /** * This is called when someone clicks on a node. either select or deselect it. @@ -39009,6 +39194,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node || Edge} object * @private */ + }, { + key: "hoverObject", value: function hoverObject(object) { var hoverChanged = false; // remove all node hover highlights @@ -39033,10 +39220,10 @@ return /******/ (function(modules) { // webpackBootstrap } // if the blur remains the same and the object is undefined (mouse off), we blur the edge else if (object === undefined) { - this.blurObject(this.hoverObj.edges[edgeId]); - delete this.hoverObj.edges[edgeId]; - hoverChanged = true; - } + this.blurObject(this.hoverObj.edges[edgeId]); + delete this.hoverObj.edges[edgeId]; + hoverChanged = true; + } } } @@ -39057,24 +39244,22 @@ return /******/ (function(modules) { // webpackBootstrap } if (hoverChanged === true) { - this.body.emitter.emit("_requestRedraw"); + this.body.emitter.emit('_requestRedraw'); } } - }, { - key: "getSelection", /** * * retrieve the currently selected objects * @return {{nodes: Array., edges: Array.}} selection */ + }, { + key: "getSelection", value: function getSelection() { var nodeIds = this.getSelectedNodes(); var edgeIds = this.getSelectedEdges(); return { nodes: nodeIds, edges: edgeIds }; } - }, { - key: "getSelectedNodes", /** * @@ -39082,6 +39267,8 @@ return /******/ (function(modules) { // webpackBootstrap * @return {String[]} selection An array with the ids of the * selected nodes. */ + }, { + key: "getSelectedNodes", value: function getSelectedNodes() { var idArray = []; if (this.options.selectable === true) { @@ -39093,8 +39280,6 @@ return /******/ (function(modules) { // webpackBootstrap } return idArray; } - }, { - key: "getSelectedEdges", /** * @@ -39102,6 +39287,8 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Array} selection An array with the ids of the * selected nodes. */ + }, { + key: "getSelectedEdges", value: function getSelectedEdges() { var idArray = []; if (this.options.selectable === true) { @@ -39113,8 +39300,6 @@ return /******/ (function(modules) { // webpackBootstrap } return idArray; } - }, { - key: "selectNodes", /** * select zero or more nodes with the option to highlight edges @@ -39122,13 +39307,15 @@ return /******/ (function(modules) { // webpackBootstrap * selected nodes. * @param {boolean} [highlightEdges] */ + }, { + key: "selectNodes", value: function selectNodes(selection) { var highlightEdges = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; var i = undefined, id = undefined; - if (!selection || selection.length === undefined) throw "Selection must be an array with ids"; + if (!selection || selection.length === undefined) throw 'Selection must be an array with ids'; // first unselect any selected node this.unselectAll(); @@ -39138,25 +39325,25 @@ return /******/ (function(modules) { // webpackBootstrap var node = this.body.nodes[id]; if (!node) { - throw new RangeError("Node with id \"" + id + "\" not found"); + throw new RangeError('Node with id "' + id + '" not found'); } this.selectObject(node, highlightEdges); } - this.body.emitter.emit("_requestRedraw"); + this.body.emitter.emit('_requestRedraw'); } - }, { - key: "selectEdges", /** * select zero or more edges * @param {Number[] | String[]} selection An array with the ids of the * selected nodes. */ + }, { + key: "selectEdges", value: function selectEdges(selection) { var i = undefined, id = undefined; - if (!selection || selection.length === undefined) throw "Selection must be an array with ids"; + if (!selection || selection.length === undefined) throw 'Selection must be an array with ids'; // first unselect any selected objects this.unselectAll(); @@ -39166,19 +39353,19 @@ return /******/ (function(modules) { // webpackBootstrap var edge = this.body.edges[id]; if (!edge) { - throw new RangeError("Edge with id \"" + id + "\" not found"); + throw new RangeError('Edge with id "' + id + '" not found'); } this.selectObject(edge); } - this.body.emitter.emit("_requestRedraw"); + this.body.emitter.emit('_requestRedraw'); } - }, { - key: "updateSelection", /** * Validate the selection: remove ids of nodes which no longer exist * @private */ + }, { + key: "updateSelection", value: function updateSelection() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -39204,7 +39391,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 109 */ +/* 110 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39219,7 +39406,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _NetworkUtil = __webpack_require__(104); + var _NetworkUtil = __webpack_require__(105); var _NetworkUtil2 = _interopRequireDefault(_NetworkUtil); @@ -39273,7 +39460,7 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options, allOptions) { if (options !== undefined) { var prevHierarchicalState = this.options.hierarchical.enabled; - util.selectiveDeepExtend(['randomSeed', 'improvedLayout'], this.options, options); + util.selectiveDeepExtend(["randomSeed", "improvedLayout"], this.options, options); util.mergeOptions(this.options, options, 'hierarchical'); if (options.randomSeed !== undefined) { this.initialRandomSeed = options.randomSeed; @@ -39395,13 +39582,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'layoutNetwork', /** * Use KamadaKawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we * cluster them first to reduce the amount. */ + }, { + key: 'layoutNetwork', value: function layoutNetwork() { if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) { // first check if we should KamadaKawai to layout. The threshold is if less than half of the visible @@ -39435,8 +39622,8 @@ return /******/ (function(modules) { // webpackBootstrap var after = this.body.nodeIndices.length; if (before == after && level % 3 !== 0 || level > MAX_LEVELS) { this._declusterAll(); - this.body.emitter.emit('_layoutFailed'); - console.info('This network could not be positioned by this version of the improved layout algorithm. Please disable improvedLayout for better performance.'); + this.body.emitter.emit("_layoutFailed"); + console.info("This network could not be positioned by this version of the improved layout algorithm. Please disable improvedLayout for better performance."); return; } //console.timeEnd("clustering") @@ -39463,17 +39650,17 @@ return /******/ (function(modules) { // webpackBootstrap this._declusterAll(); // reposition all bezier nodes. - this.body.emitter.emit('_repositionBezierNodes'); + this.body.emitter.emit("_repositionBezierNodes"); } } } - }, { - key: '_shiftToCenter', /** * Move all the nodes towards to the center so gravitational pull wil not move the nodes away from view * @private */ + }, { + key: '_shiftToCenter', value: function _shiftToCenter() { var range = _NetworkUtil2['default']._getRangeCore(this.body.nodes, this.body.nodeIndices); var center = _NetworkUtil2['default']._findCenter(range); @@ -39504,8 +39691,6 @@ return /******/ (function(modules) { // webpackBootstrap value: function getSeed() { return this.initialRandomSeed; } - }, { - key: 'setupHierarchicalLayout', /** * This is the main function to layout the nodes in a hierarchical way. @@ -39513,6 +39698,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: 'setupHierarchicalLayout', value: function setupHierarchicalLayout() { if (this.options.hierarchical.enabled === true && this.body.nodeIndices.length > 0) { // get the size of the largest hubs and check if the user has defined a level for a node. @@ -39560,8 +39747,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_placeNodesByHierarchy', /** * This function places the nodes on the canvas based on the hierarchial distribution. @@ -39569,6 +39754,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Object} distribution | obtained by the function this._getDistribution() * @private */ + }, { + key: '_placeNodesByHierarchy', value: function _placeNodesByHierarchy(distribution) { var nodeId = undefined, node = undefined; @@ -39600,8 +39787,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_getDistribution', /** * This function get the distribution of levels based on hubsize @@ -39609,6 +39794,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {Object} * @private */ + }, { + key: '_getDistribution', value: function _getDistribution() { var distribution = {}; var nodeId = undefined, @@ -39636,8 +39823,6 @@ return /******/ (function(modules) { // webpackBootstrap } return distribution; } - }, { - key: '_getHubSize', /** * Get the hubsize from all remaining unlevelled nodes. @@ -39645,6 +39830,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ + }, { + key: '_getHubSize', value: function _getHubSize() { var hubSize = 0; for (var nodeId in this.body.nodes) { @@ -39657,8 +39844,6 @@ return /******/ (function(modules) { // webpackBootstrap } return hubSize; } - }, { - key: '_determineLevelsByHubsize', /** * this function allocates nodes in levels based on the recursive branching from the largest hubs. @@ -39666,6 +39851,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param hubsize * @private */ + }, { + key: '_determineLevelsByHubsize', value: function _determineLevelsByHubsize() { var nodeId = undefined, node = undefined; @@ -39686,8 +39873,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_setLevelByHubsize', /** * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. @@ -39697,6 +39882,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentId * @private */ + }, { + key: '_setLevelByHubsize', value: function _setLevelByHubsize(level, node) { if (this.hierarchicalLevels[node.id] !== undefined) return; @@ -39711,8 +39898,6 @@ return /******/ (function(modules) { // webpackBootstrap this._setLevelByHubsize(level + 1, childNode); } } - }, { - key: '_determineLevelsDirected', /** * this function allocates nodes in levels based on the direction of the edges @@ -39720,6 +39905,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param hubsize * @private */ + }, { + key: '_determineLevelsDirected', value: function _determineLevelsDirected() { var nodeId = undefined, node = undefined; @@ -39747,8 +39934,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_setLevelDirected', /** * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction @@ -39758,6 +39943,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentId * @private */ + }, { + key: '_setLevelDirected', value: function _setLevelDirected(level, node) { if (this.hierarchicalLevels[node.id] !== undefined) return; @@ -39774,8 +39961,6 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: '_placeBranchNodes', /** * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes @@ -39787,6 +39972,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentLevel * @private */ + }, { + key: '_placeBranchNodes', value: function _placeBranchNodes(edges, parentId, distribution, parentLevel) { for (var i = 0; i < edges.length; i++) { var childNode = undefined; @@ -39833,7 +40020,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 110 */ +/* 111 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39908,13 +40095,13 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.on('_resetData', this._restore.bind(this)); } + /** + * If something changes in the data during editing, switch back to the initial datamanipulation state and close all edit modes. + * @private + */ + _createClass(ManipulationSystem, [{ key: '_restore', - - /** - * If something changes in the data during editing, switch back to the initial datamanipulation state and close all edit modes. - * @private - */ value: function _restore() { if (this.inMode !== false) { if (this.options.initiallyActive === true) { @@ -39924,13 +40111,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: 'setOptions', /** * Set the Options * @param options */ + }, { + key: 'setOptions', value: function setOptions(options, allOptions, globalOptions) { if (allOptions !== undefined) { if (allOptions.locale !== undefined) { @@ -39958,14 +40145,14 @@ return /******/ (function(modules) { // webpackBootstrap this._setup(); } } - }, { - key: 'toggleEditMode', /** * Enable or disable edit-mode. Draws the DOM required and cleans up after itself. * * @private */ + }, { + key: 'toggleEditMode', value: function toggleEditMode() { if (this.editMode === true) { this.disableEditMode(); @@ -39999,14 +40186,14 @@ return /******/ (function(modules) { // webpackBootstrap this._createEditButton(); } } - }, { - key: 'showManipulatorToolbar', /** * Creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. * * @private */ + }, { + key: 'showManipulatorToolbar', value: function showManipulatorToolbar() { // restore the state of any bound functions or events, remove control nodes, restore physics this._clean(); @@ -40081,14 +40268,14 @@ return /******/ (function(modules) { // webpackBootstrap // redraw to show any possible changes this.body.emitter.emit('_redraw'); } - }, { - key: 'addNodeMode', /** * Create the toolbar for adding Nodes * * @private */ + }, { + key: 'addNodeMode', value: function addNodeMode() { // when using the gui, enable edit mode if it wasnt already. if (this.editMode !== true) { @@ -40112,14 +40299,14 @@ return /******/ (function(modules) { // webpackBootstrap this._temporaryBindEvent('click', this._performAddNode.bind(this)); } - }, { - key: 'editNode', /** * call the bound function to handle the editing of the node. The node has to be selected. * * @private */ + }, { + key: 'editNode', value: function editNode() { var _this2 = this; @@ -40160,14 +40347,14 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } - }, { - key: 'addEdgeMode', /** * create the toolbar to connect nodes * * @private */ + }, { + key: 'addEdgeMode', value: function addEdgeMode() { // when using the gui, enable edit mode if it wasnt already. if (this.editMode !== true) { @@ -40198,14 +40385,14 @@ return /******/ (function(modules) { // webpackBootstrap this._temporaryBindUI('onDragStart', function () {}); this._temporaryBindUI('onHold', function () {}); } - }, { - key: 'editEdgeMode', /** * create the toolbar to edit edges * * @private */ + }, { + key: 'editEdgeMode', value: function editEdgeMode() { var _this3 = this; @@ -40275,14 +40462,14 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } - }, { - key: 'deleteSelected', /** * delete everything in the selection * * @private */ + }, { + key: 'deleteSelected', value: function deleteSelected() { var _this4 = this; @@ -40340,8 +40527,6 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } - }, { - key: '_setup', //********************************************** PRIVATE ***************************************// @@ -40349,6 +40534,8 @@ return /******/ (function(modules) { // webpackBootstrap * draw or remove the DOM * @private */ + }, { + key: '_setup', value: function _setup() { if (this.options.enabled === true) { // Enable the GUI @@ -40367,13 +40554,13 @@ return /******/ (function(modules) { // webpackBootstrap this.guiEnabled = false; } } - }, { - key: '_createWrappers', /** * create the div overlays that contain the DOM * @private */ + }, { + key: '_createWrappers', value: function _createWrappers() { // load the manipulator HTML elements. All styling done in css. if (this.manipulationDiv === undefined) { @@ -40407,8 +40594,6 @@ return /******/ (function(modules) { // webpackBootstrap this.canvas.frame.appendChild(this.closeDiv); } } - }, { - key: '_getNewTargetNode', /** * generate a new target node. Used for creating new edges and editing edges @@ -40417,6 +40602,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ + }, { + key: '_getNewTargetNode', value: function _getNewTargetNode(x, y) { var controlNodeStyle = util.deepExtend({}, this.options.controlNodeStyle); @@ -40428,12 +40615,12 @@ return /******/ (function(modules) { // webpackBootstrap return this.body.functions.createNode(controlNodeStyle); } - }, { - key: '_createEditButton', /** * Create the edit button */ + }, { + key: '_createEditButton', value: function _createEditButton() { // restore everything to it's original state (if applicable) this._clean(); @@ -40452,13 +40639,13 @@ return /******/ (function(modules) { // webpackBootstrap // bind a hammer listener to the button, calling the function toggleEditMode. this._bindHammerToDiv(button, this.toggleEditMode.bind(this)); } - }, { - key: '_clean', /** * this function cleans up after everything this module does. Temporary elements, functions and events are removed, physics restored, hammers removed. * @private */ + }, { + key: '_clean', value: function _clean() { // not in mode this.inMode = false; @@ -40484,13 +40671,13 @@ return /******/ (function(modules) { // webpackBootstrap // restore the physics if required this.body.emitter.emit('restorePhysics'); } - }, { - key: '_cleanManipulatorHammers', /** * Each dom element has it's own hammer. They are stored in this.manipulationHammers. This cleans them up. * @private */ + }, { + key: '_cleanManipulatorHammers', value: function _cleanManipulatorHammers() { // _clean hammer bindings if (this.manipulationHammers.length != 0) { @@ -40500,13 +40687,13 @@ return /******/ (function(modules) { // webpackBootstrap this.manipulationHammers = []; } } - }, { - key: '_removeManipulationDOM', /** * Remove all DOM elements created by this module. * @private */ + }, { + key: '_removeManipulationDOM', value: function _removeManipulationDOM() { // removes all the bindings and overloads this._clean(); @@ -40532,14 +40719,14 @@ return /******/ (function(modules) { // webpackBootstrap this.editModeDiv = undefined; this.closeDiv = undefined; } - }, { - key: '_createSeperator', /** * create a seperator line. the index is to differentiate in the manipulation dom * @param index * @private */ + }, { + key: '_createSeperator', value: function _createSeperator() { var index = arguments.length <= 0 || arguments[0] === undefined ? 1 : arguments[0]; @@ -40547,11 +40734,11 @@ return /******/ (function(modules) { // webpackBootstrap this.manipulationDOM['seperatorLineDiv' + index].className = 'vis-separator-line'; this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv' + index]); } - }, { - key: '_createAddNodeButton', // ---------------------- DOM functions for buttons --------------------------// + }, { + key: '_createAddNodeButton', value: function _createAddNodeButton(locale) { var button = this._createButton('addNode', 'vis-button vis-add', locale['addNode'] || this.options.locales['en']['addNode']); this.manipulationDiv.appendChild(button); @@ -40610,8 +40797,6 @@ return /******/ (function(modules) { // webpackBootstrap value: function _createDescription(label) { this.manipulationDiv.appendChild(this._createButton('description', 'vis-button vis-none', label)); } - }, { - key: '_temporaryBindEvent', // -------------------------- End of DOM functions for buttons ------------------------------// @@ -40621,12 +40806,12 @@ return /******/ (function(modules) { // webpackBootstrap * @param newFunction * @private */ + }, { + key: '_temporaryBindEvent', value: function _temporaryBindEvent(event, newFunction) { this.temporaryEventFunctions.push({ event: event, boundFunction: newFunction }); this.body.emitter.on(event, newFunction); } - }, { - key: '_temporaryBindUI', /** * this overrides an UI function until cleanup by the clean function @@ -40634,6 +40819,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param newFunction * @private */ + }, { + key: '_temporaryBindUI', value: function _temporaryBindUI(UIfunctionName, newFunction) { if (this.body.eventListeners[UIfunctionName] !== undefined) { this.temporaryUIFunctions[UIfunctionName] = this.body.eventListeners[UIfunctionName]; @@ -40642,14 +40829,14 @@ return /******/ (function(modules) { // webpackBootstrap throw new Error('This UI function does not exist. Typo? You tried: ' + UIfunctionName + ' possible are: ' + JSON.stringify(Object.keys(this.body.eventListeners))); } } - }, { - key: '_unbindTemporaryUIs', /** * Restore the overridden UI functions to their original state. * * @private */ + }, { + key: '_unbindTemporaryUIs', value: function _unbindTemporaryUIs() { for (var functionName in this.temporaryUIFunctions) { if (this.temporaryUIFunctions.hasOwnProperty(functionName)) { @@ -40659,13 +40846,13 @@ return /******/ (function(modules) { // webpackBootstrap } this.temporaryUIFunctions = {}; } - }, { - key: '_unbindTemporaryEvents', /** * Unbind the events created by _temporaryBindEvent * @private */ + }, { + key: '_unbindTemporaryEvents', value: function _unbindTemporaryEvents() { for (var i = 0; i < this.temporaryEventFunctions.length; i++) { var eventName = this.temporaryEventFunctions[i].event; @@ -40674,26 +40861,26 @@ return /******/ (function(modules) { // webpackBootstrap } this.temporaryEventFunctions = []; } - }, { - key: '_bindHammerToDiv', /** * Bind an hammer instance to a DOM element. * @param domElement * @param funct */ + }, { + key: '_bindHammerToDiv', value: function _bindHammerToDiv(domElement, boundFunction) { var hammer = new Hammer(domElement, {}); hammerUtil.onTouch(hammer, boundFunction); this.manipulationHammers.push(hammer); } - }, { - key: '_cleanupTemporaryNodesAndEdges', /** * Neatly clean up temporary edges and nodes * @private */ + }, { + key: '_cleanupTemporaryNodesAndEdges', value: function _cleanupTemporaryNodesAndEdges() { // _clean temporary edges for (var i = 0; i < this.temporaryIds.edges.length; i++) { @@ -40716,8 +40903,6 @@ return /******/ (function(modules) { // webpackBootstrap this.temporaryIds = { nodes: [], edges: [] }; } - }, { - key: '_controlNodeTouch', // ------------------------------------------ EDIT EDGE FUNCTIONS -----------------------------------------// @@ -40726,19 +40911,21 @@ return /******/ (function(modules) { // webpackBootstrap * @param event * @private */ + }, { + key: '_controlNodeTouch', value: function _controlNodeTouch(event) { this.selectionHandler.unselectAll(); this.lastTouch = this.body.functions.getPointer(event.center); this.lastTouch.translation = util.extend({}, this.body.view.translation); // copy the object } - }, { - key: '_controlNodeDragStart', /** * the drag start is used to mark one of the control nodes as selected. * @param event * @private */ + }, { + key: '_controlNodeDragStart', value: function _controlNodeDragStart(event) { var pointer = this.lastTouch; var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); @@ -40760,14 +40947,14 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_redraw'); } - }, { - key: '_controlNodeDrag', /** * dragging the control nodes or the canvas * @param event * @private */ + }, { + key: '_controlNodeDrag', value: function _controlNodeDrag(event) { this.body.emitter.emit('disablePhysics'); var pointer = this.body.functions.getPointer(event.center); @@ -40784,14 +40971,14 @@ return /******/ (function(modules) { // webpackBootstrap } this.body.emitter.emit('_redraw'); } - }, { - key: '_controlNodeDragEnd', /** * connecting or restoring the control nodes. * @param event * @private */ + }, { + key: '_controlNodeDragEnd', value: function _controlNodeDragEnd(event) { var pointer = this.body.functions.getPointer(event.center); var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); @@ -40829,8 +41016,6 @@ return /******/ (function(modules) { // webpackBootstrap } this.body.emitter.emit('_redraw'); } - }, { - key: '_handleConnect', // ------------------------------------ END OF EDIT EDGE FUNCTIONS -----------------------------------------// @@ -40841,6 +41026,8 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ + }, { + key: '_handleConnect', value: function _handleConnect(event) { // check to avoid double fireing of this function. if (new Date().valueOf() - this.touchTime > 100) { @@ -40896,14 +41083,14 @@ return /******/ (function(modules) { // webpackBootstrap this.body.view.translation = { x: this.lastTouch.translation.x + diffX, y: this.lastTouch.translation.y + diffY }; } } - }, { - key: '_finishConnect', /** * Connect the new edge to the target if one exists, otherwise remove temp line * @param event * @private */ + }, { + key: '_finishConnect', value: function _finishConnect(event) { var pointer = this.body.functions.getPointer(event.center); var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); @@ -40940,8 +41127,6 @@ return /******/ (function(modules) { // webpackBootstrap } this.body.emitter.emit('_redraw'); } - }, { - key: '_performAddNode', // --------------------------------------- END OF ADD EDGE FUNCTIONS -------------------------------------// @@ -40950,6 +41135,8 @@ return /******/ (function(modules) { // webpackBootstrap /** * Adds a node on the specified location */ + }, { + key: '_performAddNode', value: function _performAddNode(clickData) { var _this5 = this; @@ -40978,14 +41165,14 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } - }, { - key: '_performAddEdge', /** * connect two nodes with a new edge. * * @private */ + }, { + key: '_performAddEdge', value: function _performAddEdge(sourceNodeId, targetNodeId) { var _this6 = this; @@ -41009,14 +41196,14 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } - }, { - key: '_performEditEdge', /** * connect two nodes with a new edge. * * @private */ + }, { + key: '_performEditEdge', value: function _performEditEdge(sourceNodeId, targetNodeId) { var _this7 = this; @@ -41052,7 +41239,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 111 */ +/* 112 */ /***/ function(module, exports) { /** @@ -41287,6 +41474,7 @@ return /******/ (function(modules) { // webpackBootstrap }, physics: { enabled: { boolean: boolean }, + useWorker: { boolean: boolean }, barnesHut: { gravitationalConstant: { number: number }, centralGravity: { number: number }, @@ -41543,6 +41731,7 @@ return /******/ (function(modules) { // webpackBootstrap solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'], timestep: [0.5, 0.01, 1, 0.01] }, + //adaptiveTimestep: true global: { locale: ['en', 'nl'] } @@ -41550,10 +41739,9 @@ return /******/ (function(modules) { // webpackBootstrap exports.allOptions = allOptions; exports.configureOptions = configureOptions; - //adaptiveTimestep: true /***/ }, -/* 112 */ +/* 113 */ /***/ function(module, exports, __webpack_require__) { /** @@ -41575,7 +41763,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var _componentsAlgorithmsFloydWarshallJs = __webpack_require__(113); + var _componentsAlgorithmsFloydWarshallJs = __webpack_require__(114); var _componentsAlgorithmsFloydWarshallJs2 = _interopRequireDefault(_componentsAlgorithmsFloydWarshallJs); @@ -41598,13 +41786,13 @@ return /******/ (function(modules) { // webpackBootstrap this.distanceSolver = new _componentsAlgorithmsFloydWarshallJs2["default"](); } + /** + * Not sure if needed but can be used to update the spring length and spring constant + * @param options + */ + _createClass(KamadaKawai, [{ key: "setOptions", - - /** - * Not sure if needed but can be used to update the spring length and spring constant - * @param options - */ value: function setOptions(options) { if (options) { if (options.springLength) { @@ -41615,14 +41803,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "solve", /** * Position the system * @param nodesArray * @param edgesArray */ + }, { + key: "solve", value: function solve(nodesArray, edgesArray) { var ignoreClusters = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; @@ -41677,14 +41865,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "_getHighestEnergyNode", /** * get the node with the highest energy * @returns {*[]} * @private */ + }, { + key: "_getHighestEnergyNode", value: function _getHighestEnergyNode(ignoreClusters) { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; @@ -41716,8 +41904,6 @@ return /******/ (function(modules) { // webpackBootstrap return [maxEnergyNodeId, maxEnergy, dE_dx_max, dE_dy_max]; } - }, { - key: "_getEnergy", /** * calculate the energy of a single node @@ -41725,6 +41911,8 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*[]} * @private */ + }, { + key: "_getEnergy", value: function _getEnergy(m) { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; @@ -41747,8 +41935,6 @@ return /******/ (function(modules) { // webpackBootstrap var delta_m = Math.sqrt(Math.pow(dE_dx, 2) + Math.pow(dE_dy, 2)); return [delta_m, dE_dx, dE_dy]; } - }, { - key: "_moveNode", /** * move the node based on it's energy @@ -41758,6 +41944,8 @@ return /******/ (function(modules) { // webpackBootstrap * @param dE_dy * @private */ + }, { + key: "_moveNode", value: function _moveNode(m, dE_dx, dE_dy) { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; @@ -41793,14 +41981,14 @@ return /******/ (function(modules) { // webpackBootstrap nodes[m].x += dx; nodes[m].y += dy; } - }, { - key: "_createL_matrix", /** * Create the L matrix: edge length times shortest path * @param D_matrix * @private */ + }, { + key: "_createL_matrix", value: function _createL_matrix(D_matrix) { var nodesArray = this.body.nodeIndices; var edgeLength = this.springLength; @@ -41813,14 +42001,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } - }, { - key: "_createK_matrix", /** * Create the K matrix: spring constants times shortest path * @param D_matrix * @private */ + }, { + key: "_createK_matrix", value: function _createK_matrix(D_matrix) { var nodesArray = this.body.nodeIndices; var edgeStrength = this.springConstant; @@ -41842,7 +42030,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 113 */ +/* 114 */ /***/ function(module, exports) { /** @@ -41913,7 +42101,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 114 */ +/* 115 */ /***/ function(module, exports) { /** @@ -42200,7 +42388,7 @@ return /******/ (function(modules) { // webpackBootstrap } /***/ }, -/* 115 */ +/* 116 */ /***/ function(module, exports) { /** @@ -42550,10 +42738,10 @@ return /******/ (function(modules) { // webpackBootstrap if (token === 'false') { token = false; // convert to boolean } else if (token === 'true') { - token = true; // convert to boolean - } else if (!isNaN(Number(token))) { - token = Number(token); // convert to number - } + token = true; // convert to boolean + } else if (!isNaN(Number(token))) { + token = Number(token); // convert to number + } tokenType = TOKENTYPE.IDENTIFIER; return; } @@ -42695,8 +42883,8 @@ return /******/ (function(modules) { // webpackBootstrap getToken(); // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " } else { - parseNodeStatement(graph, id); - } + parseNodeStatement(graph, id); + } } /** @@ -43098,7 +43286,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.DOTToGraph = DOTToGraph; /***/ }, -/* 116 */ +/* 117 */ /***/ function(module, exports) { 'use strict'; @@ -43176,7 +43364,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.parseGephi = parseGephi; /***/ }, -/* 117 */ +/* 118 */ /***/ function(module, exports) { /** @@ -43202,13 +43390,13 @@ return /******/ (function(modules) { // webpackBootstrap this.callback = callback; } + /** + * @param {string} url The Url to cache the image as + * @return {Image} imageToLoadBrokenUrlOn The image object + */ + _createClass(Images, [{ key: "_addImageToCache", - - /** - * @param {string} url The Url to cache the image as - * @return {Image} imageToLoadBrokenUrlOn The image object - */ value: function _addImageToCache(url, imageToCache) { // IE11 fix -- thanks dponch! if (imageToCache.width === 0) { @@ -43220,14 +43408,14 @@ return /******/ (function(modules) { // webpackBootstrap this.images[url] = imageToCache; } - }, { - key: "_tryloadBrokenUrl", /** * @param {string} url The original Url that failed to load, if the broken image is successfully loaded it will be added to the cache using this Url as the key so that subsequent requests for this Url will return the broken image * @param {string} brokenUrl Url the broken image to try and load * @return {Image} imageToLoadBrokenUrlOn The image object */ + }, { + key: "_tryloadBrokenUrl", value: function _tryloadBrokenUrl(url, brokenUrl, imageToLoadBrokenUrlOn) { var _this = this; @@ -43244,25 +43432,25 @@ return /******/ (function(modules) { // webpackBootstrap //Set the source of the image to the brokenUrl, this is actually what kicks off the loading of the broken image imageToLoadBrokenUrlOn.src = brokenUrl; } - }, { - key: "_redrawWithImage", /** * @return {Image} imageToRedrawWith The images that will be passed to the callback when it is invoked */ + }, { + key: "_redrawWithImage", value: function _redrawWithImage(imageToRedrawWith) { if (this.callback) { this.callback(imageToRedrawWith); } } - }, { - key: "load", /** * @param {string} url Url of the image * @param {string} brokenUrl Url of an image to use if the url image is not found * @return {Image} img The image object */ + }, { + key: "load", value: function load(url, brokenUrl, id) { var _this2 = this; @@ -43302,7 +43490,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 118 */ +/* 119 */ /***/ function(module, exports) { // English diff --git a/dist/vis.min.css b/dist/vis.min.css index 554e3b1e..5779b369 100644 --- a/dist/vis.min.css +++ b/dist/vis.min.css @@ -1 +1 @@ -.vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:9pt}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:90pt;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:75pt}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;height:13px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:transparent;width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:1pc;width:1pc;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:1pc;width:1pc;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:9pt;margin-top:-9pt}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{background:url(img/timeline/delete.png) center no-repeat;position:absolute;width:24px;height:24px;top:-4px;right:-24px;cursor:pointer}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:9pt;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;font-color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;margin-top:-140px;margin-left:30px;width:293px;height:425px;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow:after,div.vis-color-picker div.vis-arrow:before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} \ No newline at end of file +.vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;height:13px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:transparent;width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{background:url(img/timeline/delete.png) center no-repeat;position:absolute;width:24px;height:24px;top:-4px;right:-24px;cursor:pointer}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;font-color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;margin-top:-140px;margin-left:30px;width:293px;height:425px;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow:after,div.vis-color-picker div.vis-arrow:before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} \ No newline at end of file diff --git a/dist/vis.physics.worker.js b/dist/vis.physics.worker.js new file mode 100644 index 00000000..3f31de26 --- /dev/null +++ b/dist/vis.physics.worker.js @@ -0,0 +1,1484 @@ +/** + * vis.js + * https://github.com/almende/vis + * + * A dynamic, browser-based visualization library. + * + * @version 4.9.1-SNAPSHOT + * @date 2015-10-05 + * + * @license + * Copyright (C) 2011-2015 Almende B.V, http://almende.com + * + * Vis.js is dual licensed under both + * + * * The Apache 2.0 License + * http://www.apache.org/licenses/LICENSE-2.0 + * + * and + * + * * The MIT License + * http://opensource.org/licenses/MIT + * + * Vis.js may be distributed under either license. + */ + +"use strict"; + +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _PhysicsWorkerJs = __webpack_require__(1); + + var _PhysicsWorkerJs2 = _interopRequireDefault(_PhysicsWorkerJs); + + var physicsWorker = new _PhysicsWorkerJs2['default'](function (data) { + return postMessage(data); + }); + self.addEventListener('message', function (event) { + return physicsWorker.handleMessage(event); + }, false); + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + 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 _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + var _componentsPhysicsBarnesHutSolver = __webpack_require__(2); + + var _componentsPhysicsBarnesHutSolver2 = _interopRequireDefault(_componentsPhysicsBarnesHutSolver); + + var _componentsPhysicsRepulsionSolver = __webpack_require__(3); + + var _componentsPhysicsRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsRepulsionSolver); + + var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(4); + + var _componentsPhysicsHierarchicalRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalRepulsionSolver); + + var _componentsPhysicsSpringSolver = __webpack_require__(5); + + var _componentsPhysicsSpringSolver2 = _interopRequireDefault(_componentsPhysicsSpringSolver); + + var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(6); + + var _componentsPhysicsHierarchicalSpringSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalSpringSolver); + + var _componentsPhysicsCentralGravitySolver = __webpack_require__(7); + + var _componentsPhysicsCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsCentralGravitySolver); + + var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(8); + + var _componentsPhysicsFA2BasedRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedRepulsionSolver); + + var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(9); + + var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver); + + var PhysicsWorker = (function () { + function PhysicsWorker(postMessage) { + _classCallCheck(this, PhysicsWorker); + + this.body = {}; + this.physicsBody = { physicsNodeIndices: [], physicsEdgeIndices: [], forces: {}, velocities: {} }; + this.postMessage = postMessage; + this.options = {}; + this.stabilized = false; + this.previousStates = {}; + this.positions = {}; + this.timestep = 0.5; + } + + _createClass(PhysicsWorker, [{ + key: 'handleMessage', + value: function handleMessage(event) { + var msg = event.data; + switch (msg.type) { + case 'calculateForces': + this.calculateForces(); + this.moveNodes(); + this.postMessage({ + type: 'positions', + data: { + positions: this.positions, + stabilized: this.stabilized + } + }); + break; + case 'update': + var node = this.body.nodes[msg.data.id]; + node.x = msg.data.x; + node.y = msg.data.y; + break; + case 'options': + this.options = msg.data; + this.timestep = this.options.timestep; + this.init(); + break; + case 'physicsObjects': + this.body.nodes = msg.data.nodes; + this.body.edges = msg.data.edges; + this.updatePhysicsData(); + break; + default: + console.warn('unknown message from PhysicsEngine', msg); + } + } + + /** + * configure the engine. + */ + }, { + key: 'init', + value: function init() { + var options; + if (this.options.solver === 'forceAtlas2Based') { + options = this.options.forceAtlas2Based; + this.nodesSolver = new _componentsPhysicsFA2BasedRepulsionSolver2['default'](this.body, this.physicsBody, options); + this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options); + this.gravitySolver = new _componentsPhysicsFA2BasedCentralGravitySolver2['default'](this.body, this.physicsBody, options); + } else if (this.options.solver === 'repulsion') { + options = this.options.repulsion; + this.nodesSolver = new _componentsPhysicsRepulsionSolver2['default'](this.body, this.physicsBody, options); + this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options); + this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options); + } else if (this.options.solver === 'hierarchicalRepulsion') { + options = this.options.hierarchicalRepulsion; + this.nodesSolver = new _componentsPhysicsHierarchicalRepulsionSolver2['default'](this.body, this.physicsBody, options); + this.edgesSolver = new _componentsPhysicsHierarchicalSpringSolver2['default'](this.body, this.physicsBody, options); + this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options); + } else { + // barnesHut + options = this.options.barnesHut; + this.nodesSolver = new _componentsPhysicsBarnesHutSolver2['default'](this.body, this.physicsBody, options); + this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options); + this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options); + } + + this.modelOptions = options; + } + + /** + * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. + * + * @private + */ + }, { + key: 'updatePhysicsData', + value: function updatePhysicsData() { + this.physicsBody.forces = {}; + this.physicsBody.physicsNodeIndices = []; + this.physicsBody.physicsEdgeIndices = []; + var nodes = this.body.nodes; + var edges = this.body.edges; + + // get node indices for physics + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + this.physicsBody.physicsNodeIndices.push(nodeId); + this.positions[nodeId] = { + x: nodes[nodeId].x, + y: nodes[nodeId].y + }; + } + } + + // get edge indices for physics + for (var edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + this.physicsBody.physicsEdgeIndices.push(edgeId); + } + } + + // get the velocity and the forces vector + for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { + var nodeId = this.physicsBody.physicsNodeIndices[i]; + this.physicsBody.forces[nodeId] = { x: 0, y: 0 }; + + // forces can be reset because they are recalculated. Velocities have to persist. + if (this.physicsBody.velocities[nodeId] === undefined) { + this.physicsBody.velocities[nodeId] = { x: 0, y: 0 }; + } + } + + // clean deleted nodes from the velocity vector + for (var nodeId in this.physicsBody.velocities) { + if (nodes[nodeId] === undefined) { + delete this.physicsBody.velocities[nodeId]; + } + } + // console.log(this.physicsBody); + } + + /** + * move the nodes one timestap and check if they are stabilized + * @returns {boolean} + */ + }, { + key: 'moveNodes', + value: function moveNodes() { + var nodeIndices = this.physicsBody.physicsNodeIndices; + var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; + var maxNodeVelocity = 0; + + for (var i = 0; i < nodeIndices.length; i++) { + var nodeId = nodeIndices[i]; + var nodeVelocity = this._performStep(nodeId, maxVelocity); + // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized + maxNodeVelocity = Math.max(maxNodeVelocity, nodeVelocity); + } + + // evaluating the stabilized and adaptiveTimestepEnabled conditions + this.stabilized = maxNodeVelocity < this.options.minVelocity; + } + + /** + * Perform the actual step + * + * @param nodeId + * @param maxVelocity + * @returns {number} + * @private + */ + }, { + key: '_performStep', + value: function _performStep(nodeId, maxVelocity) { + var node = this.body.nodes[nodeId]; + var timestep = this.timestep; + var forces = this.physicsBody.forces; + var velocities = this.physicsBody.velocities; + + // store the state so we can revert + this.previousStates[nodeId] = { x: node.x, y: node.y, vx: velocities[nodeId].x, vy: velocities[nodeId].y }; + + if (node.options.fixed.x === false) { + var dx = this.modelOptions.damping * velocities[nodeId].x; // damping force + var ax = (forces[nodeId].x - dx) / node.options.mass; // acceleration + velocities[nodeId].x += ax * timestep; // velocity + velocities[nodeId].x = Math.abs(velocities[nodeId].x) > maxVelocity ? velocities[nodeId].x > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].x; + node.x += velocities[nodeId].x * timestep; // position + this.positions[nodeId].x = node.x; + } else { + forces[nodeId].x = 0; + velocities[nodeId].x = 0; + } + + if (node.options.fixed.y === false) { + var dy = this.modelOptions.damping * velocities[nodeId].y; // damping force + var ay = (forces[nodeId].y - dy) / node.options.mass; // acceleration + velocities[nodeId].y += ay * timestep; // velocity + velocities[nodeId].y = Math.abs(velocities[nodeId].y) > maxVelocity ? velocities[nodeId].y > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].y; + node.y += velocities[nodeId].y * timestep; // position + this.positions[nodeId].y = node.y; + } else { + forces[nodeId].y = 0; + velocities[nodeId].y = 0; + } + + var totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x, 2) + Math.pow(velocities[nodeId].y, 2)); + return totalVelocity; + } + + /** + * calculate the forces for one physics iteration. + */ + }, { + key: 'calculateForces', + value: function calculateForces() { + this.gravitySolver.solve(); + this.nodesSolver.solve(); + this.edgesSolver.solve(); + } + }]); + + return PhysicsWorker; + })(); + + exports['default'] = PhysicsWorker; + module.exports = exports['default']; + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 BarnesHutSolver = (function () { + function BarnesHutSolver(body, physicsBody, options) { + _classCallCheck(this, BarnesHutSolver); + + this.body = body; + this.physicsBody = physicsBody; + this.barnesHutTree; + this.setOptions(options); + this.randomSeed = 5; + } + + _createClass(BarnesHutSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + this.thetaInversed = 1 / this.options.theta; + this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius + } + }, { + key: "seededRandom", + value: function seededRandom() { + var x = Math.sin(this.randomSeed++) * 10000; + return x - Math.floor(x); + } + + /** + * This function calculates the forces the nodes apply on eachother based on a gravitational model. + * The Barnes Hut method is used to speed up this N-body simulation. + * + * @private + */ + }, { + key: "solve", + value: function solve() { + if (this.options.gravitationalConstant !== 0 && this.physicsBody.physicsNodeIndices.length > 0) { + var node = undefined; + var nodes = this.body.nodes; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var nodeCount = nodeIndices.length; + + // create the tree + var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices); + + // for debugging + this.barnesHutTree = barnesHutTree; + + // place the nodes one by one recursively + for (var i = 0; i < nodeCount; i++) { + node = nodes[nodeIndices[i]]; + if (node.options.mass > 0) { + // starting with root is irrelevant, it never passes the BarnesHutSolver condition + this._getForceContribution(barnesHutTree.root.children.NW, node); + this._getForceContribution(barnesHutTree.root.children.NE, node); + this._getForceContribution(barnesHutTree.root.children.SW, node); + this._getForceContribution(barnesHutTree.root.children.SE, node); + } + } + } + } + + /** + * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. + * If a region contains a single node, we check if it is not itself, then we apply the force. + * + * @param parentBranch + * @param node + * @private + */ + }, { + key: "_getForceContribution", + value: function _getForceContribution(parentBranch, node) { + // we get no force contribution from an empty region + if (parentBranch.childrenCount > 0) { + var dx = undefined, + dy = undefined, + distance = undefined; + + // get the distance from the center of mass to the node. + dx = parentBranch.centerOfMass.x - node.x; + dy = parentBranch.centerOfMass.y - node.y; + distance = Math.sqrt(dx * dx + dy * dy); + + // BarnesHutSolver condition + // original condition : s/d < theta = passed === d/s > 1/theta = passed + // calcSize = 1/s --> d * 1/s > 1/theta = passed + if (distance * parentBranch.calcSize > this.thetaInversed) { + this._calculateForces(distance, dx, dy, node, parentBranch); + } else { + // Did not pass the condition, go into children if available + if (parentBranch.childrenCount === 4) { + this._getForceContribution(parentBranch.children.NW, node); + this._getForceContribution(parentBranch.children.NE, node); + this._getForceContribution(parentBranch.children.SW, node); + this._getForceContribution(parentBranch.children.SE, node); + } else { + // parentBranch must have only one node, if it was empty we wouldnt be here + if (parentBranch.children.data.id != node.id) { + // if it is not self + this._calculateForces(distance, dx, dy, node, parentBranch); + } + } + } + } + } + + /** + * Calculate the forces based on the distance. + * + * @param distance + * @param dx + * @param dy + * @param node + * @param parentBranch + * @private + */ + }, { + key: "_calculateForces", + value: function _calculateForces(distance, dx, dy, node, parentBranch) { + if (distance === 0) { + distance = 0.1; + dx = distance; + } + + if (this.overlapAvoidanceFactor < 1) { + distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); + } + + // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines + // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce + var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance, 3); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + + this.physicsBody.forces[node.id].x += fx; + this.physicsBody.forces[node.id].y += fy; + } + + /** + * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * + * @param nodes + * @param nodeIndices + * @private + */ + }, { + key: "_formBarnesHutTree", + value: function _formBarnesHutTree(nodes, nodeIndices) { + var node = undefined; + var nodeCount = nodeIndices.length; + + var minX = nodes[nodeIndices[0]].x; + var minY = nodes[nodeIndices[0]].y; + var maxX = nodes[nodeIndices[0]].x; + var maxY = nodes[nodeIndices[0]].y; + + // get the range of the nodes + for (var i = 1; i < nodeCount; i++) { + var x = nodes[nodeIndices[i]].x; + var y = nodes[nodeIndices[i]].y; + if (nodes[nodeIndices[i]].options.mass > 0) { + if (x < minX) { + minX = x; + } + if (x > maxX) { + maxX = x; + } + if (y < minY) { + minY = y; + } + if (y > maxY) { + maxY = y; + } + } + } + // make the range a square + var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y + if (sizeDiff > 0) { + minY -= 0.5 * sizeDiff; + maxY += 0.5 * sizeDiff; + } // xSize > ySize + else { + minX += 0.5 * sizeDiff; + maxX -= 0.5 * sizeDiff; + } // xSize < ySize + + var minimumTreeSize = 1e-5; + var rootSize = Math.max(minimumTreeSize, Math.abs(maxX - minX)); + var halfRootSize = 0.5 * rootSize; + var centerX = 0.5 * (minX + maxX), + centerY = 0.5 * (minY + maxY); + + // construct the barnesHutTree + var barnesHutTree = { + root: { + centerOfMass: { x: 0, y: 0 }, + mass: 0, + range: { + minX: centerX - halfRootSize, maxX: centerX + halfRootSize, + minY: centerY - halfRootSize, maxY: centerY + halfRootSize + }, + size: rootSize, + calcSize: 1 / rootSize, + children: { data: null }, + maxWidth: 0, + level: 0, + childrenCount: 4 + } + }; + this._splitBranch(barnesHutTree.root); + + // place the nodes one by one recursively + for (var i = 0; i < nodeCount; i++) { + node = nodes[nodeIndices[i]]; + if (node.options.mass > 0) { + this._placeInTree(barnesHutTree.root, node); + } + } + + // make global + return barnesHutTree; + } + + /** + * this updates the mass of a branch. this is increased by adding a node. + * + * @param parentBranch + * @param node + * @private + */ + }, { + key: "_updateBranchMass", + value: function _updateBranchMass(parentBranch, node) { + var totalMass = parentBranch.mass + node.options.mass; + var totalMassInv = 1 / totalMass; + + parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.options.mass; + parentBranch.centerOfMass.x *= totalMassInv; + + parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.options.mass; + parentBranch.centerOfMass.y *= totalMassInv; + + parentBranch.mass = totalMass; + var biggestSize = Math.max(Math.max(node.height, node.radius), node.width); + parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth; + } + + /** + * determine in which branch the node will be placed. + * + * @param parentBranch + * @param node + * @param skipMassUpdate + * @private + */ + }, { + key: "_placeInTree", + value: function _placeInTree(parentBranch, node, skipMassUpdate) { + if (skipMassUpdate != true || skipMassUpdate === undefined) { + // update the mass of the branch. + this._updateBranchMass(parentBranch, node); + } + + if (parentBranch.children.NW.range.maxX > node.x) { + // in NW or SW + if (parentBranch.children.NW.range.maxY > node.y) { + // in NW + this._placeInRegion(parentBranch, node, "NW"); + } else { + // in SW + this._placeInRegion(parentBranch, node, "SW"); + } + } else { + // in NE or SE + if (parentBranch.children.NW.range.maxY > node.y) { + // in NE + this._placeInRegion(parentBranch, node, "NE"); + } else { + // in SE + this._placeInRegion(parentBranch, node, "SE"); + } + } + } + + /** + * actually place the node in a region (or branch) + * + * @param parentBranch + * @param node + * @param region + * @private + */ + }, { + key: "_placeInRegion", + value: function _placeInRegion(parentBranch, node, region) { + switch (parentBranch.children[region].childrenCount) { + case 0: + // place node here + parentBranch.children[region].children.data = node; + parentBranch.children[region].childrenCount = 1; + this._updateBranchMass(parentBranch.children[region], node); + break; + case 1: + // convert into children + // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) + // we move one node a pixel and we do not put it in the tree. + if (parentBranch.children[region].children.data.x === node.x && parentBranch.children[region].children.data.y === node.y) { + node.x += this.seededRandom(); + node.y += this.seededRandom(); + } else { + this._splitBranch(parentBranch.children[region]); + this._placeInTree(parentBranch.children[region], node); + } + break; + case 4: + // place in branch + this._placeInTree(parentBranch.children[region], node); + break; + } + } + + /** + * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch + * after the split is complete. + * + * @param parentBranch + * @private + */ + }, { + key: "_splitBranch", + value: function _splitBranch(parentBranch) { + // if the branch is shaded with a node, replace the node in the new subset. + var containedNode = null; + if (parentBranch.childrenCount === 1) { + containedNode = parentBranch.children.data; + parentBranch.mass = 0; + parentBranch.centerOfMass.x = 0; + parentBranch.centerOfMass.y = 0; + } + parentBranch.childrenCount = 4; + parentBranch.children.data = null; + this._insertRegion(parentBranch, "NW"); + this._insertRegion(parentBranch, "NE"); + this._insertRegion(parentBranch, "SW"); + this._insertRegion(parentBranch, "SE"); + + if (containedNode != null) { + this._placeInTree(parentBranch, containedNode); + } + } + + /** + * This function subdivides the region into four new segments. + * Specifically, this inserts a single new segment. + * It fills the children section of the parentBranch + * + * @param parentBranch + * @param region + * @param parentRange + * @private + */ + }, { + key: "_insertRegion", + value: function _insertRegion(parentBranch, region) { + var minX = undefined, + maxX = undefined, + minY = undefined, + maxY = undefined; + var childSize = 0.5 * parentBranch.size; + switch (region) { + case "NW": + minX = parentBranch.range.minX; + maxX = parentBranch.range.minX + childSize; + minY = parentBranch.range.minY; + maxY = parentBranch.range.minY + childSize; + break; + case "NE": + minX = parentBranch.range.minX + childSize; + maxX = parentBranch.range.maxX; + minY = parentBranch.range.minY; + maxY = parentBranch.range.minY + childSize; + break; + case "SW": + minX = parentBranch.range.minX; + maxX = parentBranch.range.minX + childSize; + minY = parentBranch.range.minY + childSize; + maxY = parentBranch.range.maxY; + break; + case "SE": + minX = parentBranch.range.minX + childSize; + maxX = parentBranch.range.maxX; + minY = parentBranch.range.minY + childSize; + maxY = parentBranch.range.maxY; + break; + } + + parentBranch.children[region] = { + centerOfMass: { x: 0, y: 0 }, + mass: 0, + range: { minX: minX, maxX: maxX, minY: minY, maxY: maxY }, + size: 0.5 * parentBranch.size, + calcSize: 2 * parentBranch.calcSize, + children: { data: null }, + maxWidth: 0, + level: parentBranch.level + 1, + childrenCount: 0 + }; + } + + //--------------------------- DEBUGGING BELOW ---------------------------// + + /** + * This function is for debugging purposed, it draws the tree. + * + * @param ctx + * @param color + * @private + */ + }, { + key: "_debug", + value: function _debug(ctx, color) { + if (this.barnesHutTree !== undefined) { + + ctx.lineWidth = 1; + + this._drawBranch(this.barnesHutTree.root, ctx, color); + } + } + + /** + * This function is for debugging purposes. It draws the branches recursively. + * + * @param branch + * @param ctx + * @param color + * @private + */ + }, { + key: "_drawBranch", + value: function _drawBranch(branch, ctx, color) { + if (color === undefined) { + color = "#FF0000"; + } + + if (branch.childrenCount === 4) { + this._drawBranch(branch.children.NW, ctx); + this._drawBranch(branch.children.NE, ctx); + this._drawBranch(branch.children.SE, ctx); + this._drawBranch(branch.children.SW, ctx); + } + ctx.strokeStyle = color; + ctx.beginPath(); + ctx.moveTo(branch.range.minX, branch.range.minY); + ctx.lineTo(branch.range.maxX, branch.range.minY); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(branch.range.maxX, branch.range.minY); + ctx.lineTo(branch.range.maxX, branch.range.maxY); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(branch.range.maxX, branch.range.maxY); + ctx.lineTo(branch.range.minX, branch.range.maxY); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(branch.range.minX, branch.range.maxY); + ctx.lineTo(branch.range.minX, branch.range.minY); + ctx.stroke(); + + /* + if (branch.mass > 0) { + ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); + ctx.stroke(); + } + */ + } + }]); + + return BarnesHutSolver; + })(); + + exports["default"] = BarnesHutSolver; + module.exports = exports["default"]; + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 RepulsionSolver = (function () { + function RepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, RepulsionSolver); + + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } + + _createClass(RepulsionSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + + /** + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + }, { + key: "solve", + value: function solve() { + var dx, dy, distance, fx, fy, repulsingForce, node1, node2; + + var nodes = this.body.nodes; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; + + // repulsing forces between nodes + var nodeDistance = this.options.nodeDistance; + + // approximation constants + var a = -2 / 3 / nodeDistance; + var b = 4 / 3; + + // we loop from i over all but the last entree in the array + // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j + for (var i = 0; i < nodeIndices.length - 1; i++) { + node1 = nodes[nodeIndices[i]]; + for (var j = i + 1; j < nodeIndices.length; j++) { + node2 = nodes[nodeIndices[j]]; + + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); + + // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping. + if (distance === 0) { + distance = 0.1 * Math.random(); + dx = distance; + } + + if (distance < 2 * nodeDistance) { + if (distance < 0.5 * nodeDistance) { + repulsingForce = 1.0; + } else { + repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / nodeDistance - 1) * steepness)) + } + repulsingForce = repulsingForce / distance; + + fx = dx * repulsingForce; + fy = dy * repulsingForce; + + forces[node1.id].x -= fx; + forces[node1.id].y -= fy; + forces[node2.id].x += fx; + forces[node2.id].y += fy; + } + } + } + } + }]); + + return RepulsionSolver; + })(); + + exports["default"] = RepulsionSolver; + module.exports = exports["default"]; + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 HierarchicalRepulsionSolver = (function () { + function HierarchicalRepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, HierarchicalRepulsionSolver); + + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } + + _createClass(HierarchicalRepulsionSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + + /** + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + }, { + key: "solve", + value: function solve() { + var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j; + + var nodes = this.body.nodes; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; + + // repulsing forces between nodes + var nodeDistance = this.options.nodeDistance; + + // we loop from i over all but the last entree in the array + // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j + for (i = 0; i < nodeIndices.length - 1; i++) { + node1 = nodes[nodeIndices[i]]; + for (j = i + 1; j < nodeIndices.length; j++) { + node2 = nodes[nodeIndices[j]]; + + // nodes only affect nodes on their level + if (node1.level === node2.level) { + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); + + var steepness = 0.05; + if (distance < nodeDistance) { + repulsingForce = -Math.pow(steepness * distance, 2) + Math.pow(steepness * nodeDistance, 2); + } else { + repulsingForce = 0; + } + // normalize force with + if (distance === 0) { + distance = 0.01; + } else { + repulsingForce = repulsingForce / distance; + } + fx = dx * repulsingForce; + fy = dy * repulsingForce; + + forces[node1.id].x -= fx; + forces[node1.id].y -= fy; + forces[node2.id].x += fx; + forces[node2.id].y += fy; + } + } + } + } + }]); + + return HierarchicalRepulsionSolver; + })(); + + exports["default"] = HierarchicalRepulsionSolver; + module.exports = exports["default"]; + +/***/ }, +/* 5 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 SpringSolver = (function () { + function SpringSolver(body, physicsBody, options) { + _classCallCheck(this, SpringSolver); + + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } + + _createClass(SpringSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + + /** + * This function calculates the springforces on the nodes, accounting for the support nodes. + * + * @private + */ + }, { + key: "solve", + value: function solve() { + var edgeLength = undefined, + edge = undefined; + var edgeIndices = this.physicsBody.physicsEdgeIndices; + var edges = this.body.edges; + var nodes = this.body.nodes; + var node1 = undefined, + node2 = undefined, + node3 = undefined; + + // forces caused by the edges, modelled as springs + for (var i = 0; i < edgeIndices.length; i++) { + edge = edges[edgeIndices[i]]; + if (edge.connected === true && edge.toId !== edge.fromId) { + // only calculate forces if nodes are in the same sector + if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) { + if (edge.edgeType.via !== undefined) { + edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; + node1 = nodes[edge.to.id]; + node2 = nodes[edge.edgeType.via.id]; + node3 = nodes[edge.from.id]; + + this._calculateSpringForce(node1, node2, 0.5 * edgeLength); + this._calculateSpringForce(node2, node3, 0.5 * edgeLength); + } else { + // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use + // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger. + edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5 : edge.options.length; + this._calculateSpringForce(nodes[edge.from.id], nodes[edge.to.id], edgeLength); + } + } + } + } + } + + /** + * This is the code actually performing the calculation for the function above. + * + * @param node1 + * @param node2 + * @param edgeLength + * @private + */ + }, { + key: "_calculateSpringForce", + value: function _calculateSpringForce(node1, node2, edgeLength) { + var dx = node1.x - node2.x; + var dy = node1.y - node2.y; + var distance = Math.max(Math.sqrt(dx * dx + dy * dy), 0.01); + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + var springForce = this.options.springConstant * (edgeLength - distance) / distance; + + var fx = dx * springForce; + var fy = dy * springForce; + + // handle the case where one node is not part of the physcis + if (this.physicsBody.forces[node1.id] !== undefined) { + this.physicsBody.forces[node1.id].x += fx; + this.physicsBody.forces[node1.id].y += fy; + } + + if (this.physicsBody.forces[node2.id] !== undefined) { + this.physicsBody.forces[node2.id].x -= fx; + this.physicsBody.forces[node2.id].y -= fy; + } + } + }]); + + return SpringSolver; + })(); + + exports["default"] = SpringSolver; + module.exports = exports["default"]; + +/***/ }, +/* 6 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 HierarchicalSpringSolver = (function () { + function HierarchicalSpringSolver(body, physicsBody, options) { + _classCallCheck(this, HierarchicalSpringSolver); + + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } + + _createClass(HierarchicalSpringSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + + /** + * This function calculates the springforces on the nodes, accounting for the support nodes. + * + * @private + */ + }, { + key: "solve", + value: function solve() { + var edgeLength, edge; + var dx, dy, fx, fy, springForce, distance; + var edges = this.body.edges; + var factor = 0.5; + + var edgeIndices = this.physicsBody.physicsEdgeIndices; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; + + // initialize the spring force counters + for (var i = 0; i < nodeIndices.length; i++) { + var nodeId = nodeIndices[i]; + forces[nodeId].springFx = 0; + forces[nodeId].springFy = 0; + } + + // forces caused by the edges, modelled as springs + for (var i = 0; i < edgeIndices.length; i++) { + edge = edges[edgeIndices[i]]; + if (edge.connected === true) { + edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; + + dx = edge.from.x - edge.to.x; + dy = edge.from.y - edge.to.y; + distance = Math.sqrt(dx * dx + dy * dy); + distance = distance === 0 ? 0.01 : distance; + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.options.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + if (edge.to.level != edge.from.level) { + if (forces[edge.toId] !== undefined) { + forces[edge.toId].springFx -= fx; + forces[edge.toId].springFy -= fy; + } + if (forces[edge.fromId] !== undefined) { + forces[edge.fromId].springFx += fx; + forces[edge.fromId].springFy += fy; + } + } else { + if (forces[edge.toId] !== undefined) { + forces[edge.toId].x -= factor * fx; + forces[edge.toId].y -= factor * fy; + } + if (forces[edge.fromId] !== undefined) { + forces[edge.fromId].x += factor * fx; + forces[edge.fromId].y += factor * fy; + } + } + } + } + + // normalize spring forces + var springForce = 1; + var springFx, springFy; + for (var i = 0; i < nodeIndices.length; i++) { + var nodeId = nodeIndices[i]; + springFx = Math.min(springForce, Math.max(-springForce, forces[nodeId].springFx)); + springFy = Math.min(springForce, Math.max(-springForce, forces[nodeId].springFy)); + + forces[nodeId].x += springFx; + forces[nodeId].y += springFy; + } + + // retain energy balance + var totalFx = 0; + var totalFy = 0; + for (var i = 0; i < nodeIndices.length; i++) { + var nodeId = nodeIndices[i]; + totalFx += forces[nodeId].x; + totalFy += forces[nodeId].y; + } + var correctionFx = totalFx / nodeIndices.length; + var correctionFy = totalFy / nodeIndices.length; + + for (var i = 0; i < nodeIndices.length; i++) { + var nodeId = nodeIndices[i]; + forces[nodeId].x -= correctionFx; + forces[nodeId].y -= correctionFy; + } + } + }]); + + return HierarchicalSpringSolver; + })(); + + exports["default"] = HierarchicalSpringSolver; + module.exports = exports["default"]; + +/***/ }, +/* 7 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 CentralGravitySolver = (function () { + function CentralGravitySolver(body, physicsBody, options) { + _classCallCheck(this, CentralGravitySolver); + + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } + + _createClass(CentralGravitySolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + }, { + key: "solve", + value: function solve() { + var dx = undefined, + dy = undefined, + distance = undefined, + node = undefined; + var nodes = this.body.nodes; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; + + for (var i = 0; i < nodeIndices.length; i++) { + var nodeId = nodeIndices[i]; + node = nodes[nodeId]; + dx = -node.x; + dy = -node.y; + distance = Math.sqrt(dx * dx + dy * dy); + + this._calculateForces(distance, dx, dy, forces, node); + } + } + + /** + * Calculate the forces based on the distance. + * @private + */ + }, { + key: "_calculateForces", + value: function _calculateForces(distance, dx, dy, forces, node) { + var gravityForce = distance === 0 ? 0 : this.options.centralGravity / distance; + forces[node.id].x = dx * gravityForce; + forces[node.id].y = dy * gravityForce; + } + }]); + + return CentralGravitySolver; + })(); + + exports["default"] = CentralGravitySolver; + module.exports = exports["default"]; + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; })(); + + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + var _BarnesHutSolver2 = __webpack_require__(2); + + var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); + + var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) { + _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); + + function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); + + _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); + } + + /** + * Calculate the forces based on the distance. + * + * @param distance + * @param dx + * @param dy + * @param node + * @param parentBranch + * @private + */ + + _createClass(ForceAtlas2BasedRepulsionSolver, [{ + key: "_calculateForces", + value: function _calculateForces(distance, dx, dy, node, parentBranch) { + if (distance === 0) { + distance = 0.1 * Math.random(); + dx = distance; + } + + if (this.overlapAvoidanceFactor < 1) { + distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); + } + + var degree = node.edges.length + 1; + // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines + // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce + var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass * degree / Math.pow(distance, 2); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + + this.physicsBody.forces[node.id].x += fx; + this.physicsBody.forces[node.id].y += fy; + } + }]); + + return ForceAtlas2BasedRepulsionSolver; + })(_BarnesHutSolver3["default"]); + + exports["default"] = ForceAtlas2BasedRepulsionSolver; + module.exports = exports["default"]; + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; })(); + + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + var _CentralGravitySolver2 = __webpack_require__(7); + + var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2); + + var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) { + _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); + + function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { + _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver); + + _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); + } + + /** + * Calculate the forces based on the distance. + * @private + */ + + _createClass(ForceAtlas2BasedCentralGravitySolver, [{ + key: "_calculateForces", + value: function _calculateForces(distance, dx, dy, forces, node) { + if (distance > 0) { + var degree = node.edges.length + 1; + var gravityForce = this.options.centralGravity * degree * node.options.mass; + forces[node.id].x = dx * gravityForce; + forces[node.id].y = dy * gravityForce; + } + } + }]); + + return ForceAtlas2BasedCentralGravitySolver; + })(_CentralGravitySolver3["default"]); + + exports["default"] = ForceAtlas2BasedCentralGravitySolver; + module.exports = exports["default"]; + +/***/ } +/******/ ]); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 360b3bd3..e5259afa 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -52,9 +52,15 @@ var webpackConfig = { wrappedContextRegExp: /$^/ }, plugins: [ bannerPlugin ], - cache: true + cache: true, //debug: true, //bail: true + worker: { + path: DIST, + output: { + filename: "vis.physics.worker.js" + } + } }; var uglifyConfig = { diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index 5c78b9fb..d8b08dfa 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -6,6 +6,7 @@ import HierarchicalSpringSolver from './components/physics/Hierarchi import CentralGravitySolver from './components/physics/CentralGravitySolver'; import ForceAtlas2BasedRepulsionSolver from './components/physics/FA2BasedRepulsionSolver'; import ForceAtlas2BasedCentralGravitySolver from './components/physics/FA2BasedCentralGravitySolver'; +import PhysicsWorker from 'worker!./PhysicsWorkerWrapper'; var util = require('../../util'); @@ -38,6 +39,7 @@ class PhysicsEngine { this.options = {}; this.defaultOptions = { enabled: true, + useWorker: false, barnesHut: { theta: 0.5, gravitationalConstant: -2000, @@ -87,6 +89,7 @@ class PhysicsEngine { util.extend(this.options, this.defaultOptions); this.timestep = 0.5; this.layoutFailed = false; + this.draggingNodes = []; this.bindEventListeners(); } @@ -111,6 +114,15 @@ class PhysicsEngine { this.body.emitter.on('destroy', () => { this.stopSimulation(false); this.body.emitter.off(); + }); + // For identifying which nodes to send to worker thread + this.body.emitter.on('dragStart', (properties) => {this.draggingNodes = properties.nodes;}); + this.body.emitter.on('dragEnd', () => {this.draggingNodes = [];}); + this.body.emitter.on('destroy', () => { + if (this.physicsWorker) { + this.physicsWorker.terminate(); + this.physicsWorker = undefined; + } }); } @@ -144,14 +156,25 @@ class PhysicsEngine { this.timestep = this.options.timestep; } } - this.init(); + if (this.options.useWorker) { + this.initPhysicsWorker(); + this.physicsWorker.postMessage({type: 'options', data: this.options}); + } else { + this.initEmbeddedPhysics(); + } } /** * configure the engine. */ - init() { + initEmbeddedPhysics() { + if (this.physicsWorker) { + this.options.useWorker = false; + this.physicsWorker.terminate(); + this.physicsWorker = undefined; + this.updatePhysicsData(); + } var options; if (this.options.solver === 'forceAtlas2Based') { options = this.options.forceAtlas2Based; @@ -181,6 +204,65 @@ class PhysicsEngine { this.modelOptions = options; } + initPhysicsWorker() { + if (!this.physicsWorker) { + if (!__webpack_public_path__) { + let parentScript = document.getElementById('visjs'); + if (parentScript) { + let src = parentScript.getAttribute('src') + __webpack_public_path__ = src.substr(0, src.lastIndexOf('/') + 1); + } else { + let scripts = document.getElementsByTagName('script'); + for (let i = 0; i < scripts.length; i++) { + let src = scripts[i].getAttribute('src'); + if (src && src.length >= 6) { + let position = src.length - 6; + let index = src.indexOf('vis.js', position); + if (index === position) { + __webpack_public_path__ = src.substr(0, src.lastIndexOf('/') + 1); + break; + } + } + } + } + } + this.physicsWorker = new PhysicsWorker(); + this.physicsWorker.addEventListener('message', (event) => { + this.physicsWorkerMessageHandler(event); + }); + this.physicsWorker.onerror = (event) => { + console.error('Falling back to embedded physics engine'); + this.initEmbeddedPhysics(); + // throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")"); + }; + } + } + + physicsWorkerMessageHandler(event) { + var msg = event.data; + switch (msg.type) { + case 'positions': + this.stabilized = msg.data.stabilized; + var positions = msg.data.positions; + // console.log('received positions', positions); + for (let i = 0; i < this.draggingNodes; i++) { + delete positions[this.draggingNodes[i]]; + } + let nodeIds = Object.keys(positions); + for (let i = 0; i < nodeIds.length; i++) { + let nodeId = nodeIds[i]; + let node = this.body.nodes[nodeId]; + // handle case where we get a positions from an old physicsObject + if (node) { + node.x = positions[nodeId].x; + node.y = positions[nodeId].y; + } + } + break; + default: + console.warn('unhandled physics worker message:', msg); + } + } /** * initialize the engine @@ -208,6 +290,20 @@ class PhysicsEngine { */ startSimulation() { if (this.physicsEnabled === true && this.options.enabled === true) { + if (this.physicsWorker) { + for(let i = 0; i < this.draggingNodes.length; i++) { + let nodeId = this.draggingNodes[i]; + let node = this.body.nodes[nodeId]; + this.physicsWorker.postMessage({ + type: 'update', + data: { + id: nodeId, + x: node.x, + y: node.y + } + }); + } + } this.stabilized = false; // when visible, adaptivity is disabled. @@ -349,8 +445,13 @@ class PhysicsEngine { else { // case for the static timestep, we reset it to the one in options and take a normal step. this.timestep = this.options.timestep; - this.calculateForces(); - this.moveNodes(); + if (this.physicsWorker) { + // console.log('asking working to do a physics iteration'); + this.physicsWorker.postMessage({type: 'calculateForces'}); + } else { + this.calculateForces(); + this.moveNodes(); + } } // determine if the network has stabilzied @@ -368,45 +469,110 @@ class PhysicsEngine { * @private */ updatePhysicsData() { - this.physicsBody.forces = {}; - this.physicsBody.physicsNodeIndices = []; - this.physicsBody.physicsEdgeIndices = []; let nodes = this.body.nodes; let edges = this.body.edges; - // get node indices for physics - for (let nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].options.physics === true) { - this.physicsBody.physicsNodeIndices.push(nodeId); + if (this.physicsWorker) { + var physicsWorkerNodes = {}; + var physicsWorkerEdges = {}; + + for (let nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + let node = nodes[nodeId]; + if (node.options.physics === true) { + physicsWorkerNodes[nodeId] = { + id: node.id, + x: node.x, + y: node.y, + options: { + fixed: { + x: node.options.fixed.x, + y: node.options.fixed.y + }, + mass: node.options.mass + } + } + } } } - } - // get edge indices for physics - for (let edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].options.physics === true) { - this.physicsBody.physicsEdgeIndices.push(edgeId); + for (let edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + let edge = edges[edgeId]; + if (edge.options.physics === true) { + physicsWorkerEdges[edgeId] = { + connected: edge.connected, + id: edge.id, + edgeType: {}, + toId: edge.toId, + fromId: edge.fromId, + to: { + id: edge.to.id + }, + from: { + id: edge.from.id + }, + options: { + length: edge.length + } + }; + if (edge.edgeType.via) { + physicsWorkerEdges[edgeId].edgeType = { + via: { + id: edge.edgeType.via.id + } + } + } + } + } + } + + this.physicsWorker.postMessage({ + type: 'physicsObjects', + data: { + nodes: physicsWorkerNodes, + edges: physicsWorkerEdges + } + }); + } else { + this.physicsBody.forces = {}; + this.physicsBody.physicsNodeIndices = []; + this.physicsBody.physicsEdgeIndices = []; + + // get node indices for physics + for (let nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + if (nodes[nodeId].options.physics === true) { + this.physicsBody.physicsNodeIndices.push(nodeId); + } + } + } + + // get edge indices for physics + for (let edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + if (edges[edgeId].options.physics === true) { + this.physicsBody.physicsEdgeIndices.push(edgeId); + } } } - } - // get the velocity and the forces vector - for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { - let nodeId = this.physicsBody.physicsNodeIndices[i]; - this.physicsBody.forces[nodeId] = {x:0,y:0}; + // get the velocity and the forces vector + for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { + let nodeId = this.physicsBody.physicsNodeIndices[i]; + this.physicsBody.forces[nodeId] = {x: 0, y: 0}; - // forces can be reset because they are recalculated. Velocities have to persist. - if (this.physicsBody.velocities[nodeId] === undefined) { - this.physicsBody.velocities[nodeId] = {x:0,y:0}; + // forces can be reset because they are recalculated. Velocities have to persist. + if (this.physicsBody.velocities[nodeId] === undefined) { + this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; + } } - } - // clean deleted nodes from the velocity vector - for (let nodeId in this.physicsBody.velocities) { - if (nodes[nodeId] === undefined) { - delete this.physicsBody.velocities[nodeId]; + // clean deleted nodes from the velocity vector + for (let nodeId in this.physicsBody.velocities) { + if (nodes[nodeId] === undefined) { + delete this.physicsBody.velocities[nodeId]; + } } } } @@ -621,7 +787,7 @@ class PhysicsEngine { this._freezeNodes(); } this.stabilizationIterations = 0; - + setTimeout(() => this._stabilizationBatch(),0); } @@ -642,7 +808,7 @@ class PhysicsEngine { this.physicsTick(); count++; } - + if (this.stabilized === false && this.stabilizationIterations < this.targetIterations) { this.body.emitter.emit('stabilizationProgress', {iterations: this.stabilizationIterations, total: this.targetIterations}); setTimeout(this._stabilizationBatch.bind(this),0); @@ -679,7 +845,7 @@ class PhysicsEngine { this.ready = true; } - + } export default PhysicsEngine; \ No newline at end of file diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js new file mode 100644 index 00000000..b6b76335 --- /dev/null +++ b/lib/network/modules/PhysicsWorker.js @@ -0,0 +1,216 @@ +import BarnesHutSolver from './components/physics/BarnesHutSolver'; +import Repulsion from './components/physics/RepulsionSolver'; +import HierarchicalRepulsion from './components/physics/HierarchicalRepulsionSolver'; +import SpringSolver from './components/physics/SpringSolver'; +import HierarchicalSpringSolver from './components/physics/HierarchicalSpringSolver'; +import CentralGravitySolver from './components/physics/CentralGravitySolver'; +import ForceAtlas2BasedRepulsionSolver from './components/physics/FA2BasedRepulsionSolver'; +import ForceAtlas2BasedCentralGravitySolver from './components/physics/FA2BasedCentralGravitySolver'; + +class PhysicsWorker { + constructor(postMessage) { + this.body = {}; + this.physicsBody = {physicsNodeIndices:[], physicsEdgeIndices:[], forces: {}, velocities: {}}; + this.postMessage = postMessage; + this.options = {}; + this.stabilized = false; + this.previousStates = {}; + this.positions = {}; + this.timestep = 0.5; + } + + handleMessage(event) { + var msg = event.data; + switch (msg.type) { + case 'calculateForces': + this.calculateForces(); + this.moveNodes(); + this.postMessage({ + type: 'positions', + data: { + positions: this.positions, + stabilized: this.stabilized + } + }); + break; + case 'update': + let node = this.body.nodes[msg.data.id]; + node.x = msg.data.x; + node.y = msg.data.y; + break; + case 'options': + this.options = msg.data; + this.timestep = this.options.timestep; + this.init(); + break; + case 'physicsObjects': + this.body.nodes = msg.data.nodes; + this.body.edges = msg.data.edges; + this.updatePhysicsData(); + break; + default: + console.warn('unknown message from PhysicsEngine', msg); + } + } + + /** + * configure the engine. + */ + init() { + var options; + if (this.options.solver === 'forceAtlas2Based') { + options = this.options.forceAtlas2Based; + this.nodesSolver = new ForceAtlas2BasedRepulsionSolver(this.body, this.physicsBody, options); + this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new ForceAtlas2BasedCentralGravitySolver(this.body, this.physicsBody, options); + } + else if (this.options.solver === 'repulsion') { + options = this.options.repulsion; + this.nodesSolver = new Repulsion(this.body, this.physicsBody, options); + this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); + } + else if (this.options.solver === 'hierarchicalRepulsion') { + options = this.options.hierarchicalRepulsion; + this.nodesSolver = new HierarchicalRepulsion(this.body, this.physicsBody, options); + this.edgesSolver = new HierarchicalSpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); + } + else { // barnesHut + options = this.options.barnesHut; + this.nodesSolver = new BarnesHutSolver(this.body, this.physicsBody, options); + this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); + } + + this.modelOptions = options; + } + + /** + * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. + * + * @private + */ + updatePhysicsData() { + this.physicsBody.forces = {}; + this.physicsBody.physicsNodeIndices = []; + this.physicsBody.physicsEdgeIndices = []; + let nodes = this.body.nodes; + let edges = this.body.edges; + + // get node indices for physics + for (let nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + this.physicsBody.physicsNodeIndices.push(nodeId); + this.positions[nodeId] = { + x: nodes[nodeId].x, + y: nodes[nodeId].y + } + } + } + + // get edge indices for physics + for (let edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + this.physicsBody.physicsEdgeIndices.push(edgeId); + } + } + + // get the velocity and the forces vector + for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { + let nodeId = this.physicsBody.physicsNodeIndices[i]; + this.physicsBody.forces[nodeId] = {x: 0, y: 0}; + + // forces can be reset because they are recalculated. Velocities have to persist. + if (this.physicsBody.velocities[nodeId] === undefined) { + this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; + } + } + + // clean deleted nodes from the velocity vector + for (let nodeId in this.physicsBody.velocities) { + if (nodes[nodeId] === undefined) { + delete this.physicsBody.velocities[nodeId]; + } + } + // console.log(this.physicsBody); + } + + /** + * move the nodes one timestap and check if they are stabilized + * @returns {boolean} + */ + moveNodes() { + var nodeIndices = this.physicsBody.physicsNodeIndices; + var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; + var maxNodeVelocity = 0; + + for (let i = 0; i < nodeIndices.length; i++) { + let nodeId = nodeIndices[i]; + let nodeVelocity = this._performStep(nodeId, maxVelocity); + // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized + maxNodeVelocity = Math.max(maxNodeVelocity,nodeVelocity); + } + + // evaluating the stabilized and adaptiveTimestepEnabled conditions + this.stabilized = maxNodeVelocity < this.options.minVelocity; + } + + /** + * Perform the actual step + * + * @param nodeId + * @param maxVelocity + * @returns {number} + * @private + */ + _performStep(nodeId,maxVelocity) { + let node = this.body.nodes[nodeId]; + let timestep = this.timestep; + let forces = this.physicsBody.forces; + let velocities = this.physicsBody.velocities; + + // store the state so we can revert + this.previousStates[nodeId] = {x:node.x, y:node.y, vx:velocities[nodeId].x, vy:velocities[nodeId].y}; + + if (node.options.fixed.x === false) { + let dx = this.modelOptions.damping * velocities[nodeId].x; // damping force + let ax = (forces[nodeId].x - dx) / node.options.mass; // acceleration + velocities[nodeId].x += ax * timestep; // velocity + velocities[nodeId].x = (Math.abs(velocities[nodeId].x) > maxVelocity) ? ((velocities[nodeId].x > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].x; + node.x += velocities[nodeId].x * timestep; // position + this.positions[nodeId].x = node.x; + } + else { + forces[nodeId].x = 0; + velocities[nodeId].x = 0; + } + + if (node.options.fixed.y === false) { + let dy = this.modelOptions.damping * velocities[nodeId].y; // damping force + let ay = (forces[nodeId].y - dy) / node.options.mass; // acceleration + velocities[nodeId].y += ay * timestep; // velocity + velocities[nodeId].y = (Math.abs(velocities[nodeId].y) > maxVelocity) ? ((velocities[nodeId].y > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].y; + node.y += velocities[nodeId].y * timestep; // position + this.positions[nodeId].y = node.y; + } + else { + forces[nodeId].y = 0; + velocities[nodeId].y = 0; + } + + let totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x,2) + Math.pow(velocities[nodeId].y,2)); + return totalVelocity; + } + + /** + * calculate the forces for one physics iteration. + */ + calculateForces() { + this.gravitySolver.solve(); + this.nodesSolver.solve(); + this.edgesSolver.solve(); + } +} + +export default PhysicsWorker; diff --git a/lib/network/modules/PhysicsWorkerWrapper.js b/lib/network/modules/PhysicsWorkerWrapper.js new file mode 100644 index 00000000..12222d3f --- /dev/null +++ b/lib/network/modules/PhysicsWorkerWrapper.js @@ -0,0 +1,4 @@ +import PhysicsWorker from './PhysicsWorker.js'; + +var physicsWorker = new PhysicsWorker((data) => postMessage(data)); +self.addEventListener('message', (event) => physicsWorker.handleMessage(event), false); diff --git a/lib/network/modules/components/physics/SpringSolver.js b/lib/network/modules/components/physics/SpringSolver.js index 194631f3..ac10617d 100644 --- a/lib/network/modules/components/physics/SpringSolver.js +++ b/lib/network/modules/components/physics/SpringSolver.js @@ -18,6 +18,7 @@ class SpringSolver { let edgeLength, edge; let edgeIndices = this.physicsBody.physicsEdgeIndices; let edges = this.body.edges; + let nodes = this.body.nodes; let node1, node2, node3; // forces caused by the edges, modelled as springs @@ -28,9 +29,9 @@ class SpringSolver { if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) { if (edge.edgeType.via !== undefined) { edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; - node1 = edge.to; - node2 = edge.edgeType.via; - node3 = edge.from; + node1 = nodes[edge.to.id]; + node2 = nodes[edge.edgeType.via.id]; + node3 = nodes[edge.from.id]; this._calculateSpringForce(node1, node2, 0.5 * edgeLength); this._calculateSpringForce(node2, node3, 0.5 * edgeLength); @@ -39,7 +40,7 @@ class SpringSolver { // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger. edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5: edge.options.length; - this._calculateSpringForce(edge.from, edge.to, edgeLength); + this._calculateSpringForce(nodes[edge.from.id], nodes[edge.to.id], edgeLength); } } } diff --git a/lib/network/options.js b/lib/network/options.js index ba435208..54071e19 100644 --- a/lib/network/options.js +++ b/lib/network/options.js @@ -226,6 +226,7 @@ let allOptions = { }, physics: { enabled: { boolean }, + useWorker: { boolean }, barnesHut: { gravitationalConstant: { number }, centralGravity: { number }, diff --git a/package.json b/package.json index c2bf6d0e..c498c76d 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "keycharm": "^0.2.0", "moment": "^2.10.2", "propagating-hammerjs": "^1.4.3", - "uuid": "^2.0.1" + "uuid": "^2.0.1", + "worker-loader": "^0.6.0" }, "devDependencies": { "babel": "^5.1.11", From 6813bcd465c54e01352537e4d3757d66a93910d0 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Tue, 6 Oct 2015 10:04:03 -0400 Subject: [PATCH 02/17] fixed whitespace and added initial documentation --- docs/network/physics.html | 11 +- lib/network/modules/PhysicsEngine.js | 96 +++---- lib/network/modules/PhysicsWorker.js | 406 +++++++++++++-------------- 3 files changed, 261 insertions(+), 252 deletions(-) diff --git a/docs/network/physics.html b/docs/network/physics.html index c8fe9920..f38fc3e9 100644 --- a/docs/network/physics.html +++ b/docs/network/physics.html @@ -139,7 +139,8 @@ var options = { fit: true }, timestep: 0.5, - adaptiveTimestep: true + adaptiveTimestep: true, + useWorker: false } } @@ -203,6 +204,14 @@ network.setOptions(options); stabilization.fit Boolean true Toggle whether or not you want the view to zoom to fit all nodes when the stabilization is finished. timestep Number 0.5 The physics simulation is discrete. This means we take a step in time, calculate the forces, move the nodes and take another step. If you increase this number the steps will be too large and the network can get unstable. If you see a lot of jittery movement in the network, you may want to reduce this value a little. adaptiveTimestep Boolean true If this is enabled, the timestep will intelligently be adapted (only during the stabilization stage if stabilization is enabled!) to greatly decrease stabilization times. The timestep configured above is taken as the minimum timestep. This can be further improved by using the improvedLayout algorithm. + useWorker Boolean false + If this is enabled, the physics calculation will be performed in a separate thread. The file vis.physics.worker.js must be available from the same webserver hosting vis.js at the same path. If + you are embedding vis into a javascript bundle, you can set the script id to "visjs" to enable + path resolution. If the worker fails for any reason, the system will fall back to standard + physics calculations. + WorkInProgress: This has only been tested with default physics selections and does not attempt to optimize + stabilization. + diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index d8b08dfa..c1ea64fe 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -89,7 +89,7 @@ class PhysicsEngine { util.extend(this.options, this.defaultOptions); this.timestep = 0.5; this.layoutFailed = false; - this.draggingNodes = []; + this.draggingNodes = []; this.bindEventListeners(); } @@ -115,9 +115,9 @@ class PhysicsEngine { this.stopSimulation(false); this.body.emitter.off(); }); - // For identifying which nodes to send to worker thread - this.body.emitter.on('dragStart', (properties) => {this.draggingNodes = properties.nodes;}); - this.body.emitter.on('dragEnd', () => {this.draggingNodes = [];}); + // For identifying which nodes to send to worker thread + this.body.emitter.on('dragStart', (properties) => {this.draggingNodes = properties.nodes;}); + this.body.emitter.on('dragEnd', () => {this.draggingNodes = [];}); this.body.emitter.on('destroy', () => { if (this.physicsWorker) { this.physicsWorker.terminate(); @@ -290,20 +290,20 @@ class PhysicsEngine { */ startSimulation() { if (this.physicsEnabled === true && this.options.enabled === true) { - if (this.physicsWorker) { - for(let i = 0; i < this.draggingNodes.length; i++) { - let nodeId = this.draggingNodes[i]; - let node = this.body.nodes[nodeId]; - this.physicsWorker.postMessage({ - type: 'update', - data: { - id: nodeId, - x: node.x, - y: node.y - } - }); - } - } + if (this.physicsWorker) { + for(let i = 0; i < this.draggingNodes.length; i++) { + let nodeId = this.draggingNodes[i]; + let node = this.body.nodes[nodeId]; + this.physicsWorker.postMessage({ + type: 'update', + data: { + id: nodeId, + x: node.x, + y: node.y + } + }); + } + } this.stabilized = false; // when visible, adaptivity is disabled. @@ -496,36 +496,36 @@ class PhysicsEngine { } } - for (let edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - let edge = edges[edgeId]; - if (edge.options.physics === true) { - physicsWorkerEdges[edgeId] = { - connected: edge.connected, - id: edge.id, - edgeType: {}, - toId: edge.toId, - fromId: edge.fromId, - to: { - id: edge.to.id - }, - from: { - id: edge.from.id - }, - options: { - length: edge.length - } - }; - if (edge.edgeType.via) { - physicsWorkerEdges[edgeId].edgeType = { - via: { - id: edge.edgeType.via.id - } - } - } - } - } - } + for (let edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + let edge = edges[edgeId]; + if (edge.options.physics === true) { + physicsWorkerEdges[edgeId] = { + connected: edge.connected, + id: edge.id, + edgeType: {}, + toId: edge.toId, + fromId: edge.fromId, + to: { + id: edge.to.id + }, + from: { + id: edge.from.id + }, + options: { + length: edge.length + } + }; + if (edge.edgeType.via) { + physicsWorkerEdges[edgeId].edgeType = { + via: { + id: edge.edgeType.via.id + } + } + } + } + } + } this.physicsWorker.postMessage({ type: 'physicsObjects', diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index b6b76335..f176a11c 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -8,209 +8,209 @@ import ForceAtlas2BasedRepulsionSolver from './components/physics/FA2BasedR import ForceAtlas2BasedCentralGravitySolver from './components/physics/FA2BasedCentralGravitySolver'; class PhysicsWorker { - constructor(postMessage) { - this.body = {}; - this.physicsBody = {physicsNodeIndices:[], physicsEdgeIndices:[], forces: {}, velocities: {}}; - this.postMessage = postMessage; - this.options = {}; - this.stabilized = false; - this.previousStates = {}; - this.positions = {}; - this.timestep = 0.5; - } - - handleMessage(event) { - var msg = event.data; - switch (msg.type) { - case 'calculateForces': - this.calculateForces(); - this.moveNodes(); - this.postMessage({ - type: 'positions', - data: { - positions: this.positions, - stabilized: this.stabilized - } - }); - break; - case 'update': - let node = this.body.nodes[msg.data.id]; - node.x = msg.data.x; - node.y = msg.data.y; - break; - case 'options': - this.options = msg.data; - this.timestep = this.options.timestep; - this.init(); - break; - case 'physicsObjects': - this.body.nodes = msg.data.nodes; - this.body.edges = msg.data.edges; - this.updatePhysicsData(); - break; - default: - console.warn('unknown message from PhysicsEngine', msg); - } - } - - /** - * configure the engine. - */ - init() { - var options; - if (this.options.solver === 'forceAtlas2Based') { - options = this.options.forceAtlas2Based; - this.nodesSolver = new ForceAtlas2BasedRepulsionSolver(this.body, this.physicsBody, options); - this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new ForceAtlas2BasedCentralGravitySolver(this.body, this.physicsBody, options); - } - else if (this.options.solver === 'repulsion') { - options = this.options.repulsion; - this.nodesSolver = new Repulsion(this.body, this.physicsBody, options); - this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); - } - else if (this.options.solver === 'hierarchicalRepulsion') { - options = this.options.hierarchicalRepulsion; - this.nodesSolver = new HierarchicalRepulsion(this.body, this.physicsBody, options); - this.edgesSolver = new HierarchicalSpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); - } - else { // barnesHut - options = this.options.barnesHut; - this.nodesSolver = new BarnesHutSolver(this.body, this.physicsBody, options); - this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); - } - - this.modelOptions = options; - } - - /** - * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. - * - * @private - */ - updatePhysicsData() { - this.physicsBody.forces = {}; - this.physicsBody.physicsNodeIndices = []; - this.physicsBody.physicsEdgeIndices = []; - let nodes = this.body.nodes; - let edges = this.body.edges; - - // get node indices for physics - for (let nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - this.physicsBody.physicsNodeIndices.push(nodeId); - this.positions[nodeId] = { - x: nodes[nodeId].x, - y: nodes[nodeId].y - } - } - } - - // get edge indices for physics - for (let edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - this.physicsBody.physicsEdgeIndices.push(edgeId); - } - } - - // get the velocity and the forces vector - for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { - let nodeId = this.physicsBody.physicsNodeIndices[i]; - this.physicsBody.forces[nodeId] = {x: 0, y: 0}; - - // forces can be reset because they are recalculated. Velocities have to persist. - if (this.physicsBody.velocities[nodeId] === undefined) { - this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; - } - } - - // clean deleted nodes from the velocity vector - for (let nodeId in this.physicsBody.velocities) { - if (nodes[nodeId] === undefined) { - delete this.physicsBody.velocities[nodeId]; - } - } - // console.log(this.physicsBody); - } - - /** - * move the nodes one timestap and check if they are stabilized - * @returns {boolean} - */ - moveNodes() { - var nodeIndices = this.physicsBody.physicsNodeIndices; - var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; - var maxNodeVelocity = 0; - - for (let i = 0; i < nodeIndices.length; i++) { - let nodeId = nodeIndices[i]; - let nodeVelocity = this._performStep(nodeId, maxVelocity); - // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized - maxNodeVelocity = Math.max(maxNodeVelocity,nodeVelocity); - } - - // evaluating the stabilized and adaptiveTimestepEnabled conditions - this.stabilized = maxNodeVelocity < this.options.minVelocity; - } - - /** - * Perform the actual step - * - * @param nodeId - * @param maxVelocity - * @returns {number} - * @private - */ - _performStep(nodeId,maxVelocity) { - let node = this.body.nodes[nodeId]; - let timestep = this.timestep; - let forces = this.physicsBody.forces; - let velocities = this.physicsBody.velocities; - - // store the state so we can revert - this.previousStates[nodeId] = {x:node.x, y:node.y, vx:velocities[nodeId].x, vy:velocities[nodeId].y}; - - if (node.options.fixed.x === false) { - let dx = this.modelOptions.damping * velocities[nodeId].x; // damping force - let ax = (forces[nodeId].x - dx) / node.options.mass; // acceleration - velocities[nodeId].x += ax * timestep; // velocity - velocities[nodeId].x = (Math.abs(velocities[nodeId].x) > maxVelocity) ? ((velocities[nodeId].x > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].x; - node.x += velocities[nodeId].x * timestep; // position - this.positions[nodeId].x = node.x; - } - else { - forces[nodeId].x = 0; - velocities[nodeId].x = 0; - } - - if (node.options.fixed.y === false) { - let dy = this.modelOptions.damping * velocities[nodeId].y; // damping force - let ay = (forces[nodeId].y - dy) / node.options.mass; // acceleration - velocities[nodeId].y += ay * timestep; // velocity - velocities[nodeId].y = (Math.abs(velocities[nodeId].y) > maxVelocity) ? ((velocities[nodeId].y > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].y; - node.y += velocities[nodeId].y * timestep; // position - this.positions[nodeId].y = node.y; - } - else { - forces[nodeId].y = 0; - velocities[nodeId].y = 0; - } - - let totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x,2) + Math.pow(velocities[nodeId].y,2)); - return totalVelocity; - } - - /** - * calculate the forces for one physics iteration. - */ - calculateForces() { - this.gravitySolver.solve(); - this.nodesSolver.solve(); - this.edgesSolver.solve(); - } + constructor(postMessage) { + this.body = {}; + this.physicsBody = {physicsNodeIndices:[], physicsEdgeIndices:[], forces: {}, velocities: {}}; + this.postMessage = postMessage; + this.options = {}; + this.stabilized = false; + this.previousStates = {}; + this.positions = {}; + this.timestep = 0.5; + } + + handleMessage(event) { + var msg = event.data; + switch (msg.type) { + case 'calculateForces': + this.calculateForces(); + this.moveNodes(); + this.postMessage({ + type: 'positions', + data: { + positions: this.positions, + stabilized: this.stabilized + } + }); + break; + case 'update': + let node = this.body.nodes[msg.data.id]; + node.x = msg.data.x; + node.y = msg.data.y; + break; + case 'options': + this.options = msg.data; + this.timestep = this.options.timestep; + this.init(); + break; + case 'physicsObjects': + this.body.nodes = msg.data.nodes; + this.body.edges = msg.data.edges; + this.updatePhysicsData(); + break; + default: + console.warn('unknown message from PhysicsEngine', msg); + } + } + + /** + * configure the engine. + */ + init() { + var options; + if (this.options.solver === 'forceAtlas2Based') { + options = this.options.forceAtlas2Based; + this.nodesSolver = new ForceAtlas2BasedRepulsionSolver(this.body, this.physicsBody, options); + this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new ForceAtlas2BasedCentralGravitySolver(this.body, this.physicsBody, options); + } + else if (this.options.solver === 'repulsion') { + options = this.options.repulsion; + this.nodesSolver = new Repulsion(this.body, this.physicsBody, options); + this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); + } + else if (this.options.solver === 'hierarchicalRepulsion') { + options = this.options.hierarchicalRepulsion; + this.nodesSolver = new HierarchicalRepulsion(this.body, this.physicsBody, options); + this.edgesSolver = new HierarchicalSpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); + } + else { // barnesHut + options = this.options.barnesHut; + this.nodesSolver = new BarnesHutSolver(this.body, this.physicsBody, options); + this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); + } + + this.modelOptions = options; + } + + /** + * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. + * + * @private + */ + updatePhysicsData() { + this.physicsBody.forces = {}; + this.physicsBody.physicsNodeIndices = []; + this.physicsBody.physicsEdgeIndices = []; + let nodes = this.body.nodes; + let edges = this.body.edges; + + // get node indices for physics + for (let nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + this.physicsBody.physicsNodeIndices.push(nodeId); + this.positions[nodeId] = { + x: nodes[nodeId].x, + y: nodes[nodeId].y + } + } + } + + // get edge indices for physics + for (let edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + this.physicsBody.physicsEdgeIndices.push(edgeId); + } + } + + // get the velocity and the forces vector + for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { + let nodeId = this.physicsBody.physicsNodeIndices[i]; + this.physicsBody.forces[nodeId] = {x: 0, y: 0}; + + // forces can be reset because they are recalculated. Velocities have to persist. + if (this.physicsBody.velocities[nodeId] === undefined) { + this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; + } + } + + // clean deleted nodes from the velocity vector + for (let nodeId in this.physicsBody.velocities) { + if (nodes[nodeId] === undefined) { + delete this.physicsBody.velocities[nodeId]; + } + } + // console.log(this.physicsBody); + } + + /** + * move the nodes one timestap and check if they are stabilized + * @returns {boolean} + */ + moveNodes() { + var nodeIndices = this.physicsBody.physicsNodeIndices; + var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; + var maxNodeVelocity = 0; + + for (let i = 0; i < nodeIndices.length; i++) { + let nodeId = nodeIndices[i]; + let nodeVelocity = this._performStep(nodeId, maxVelocity); + // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized + maxNodeVelocity = Math.max(maxNodeVelocity,nodeVelocity); + } + + // evaluating the stabilized and adaptiveTimestepEnabled conditions + this.stabilized = maxNodeVelocity < this.options.minVelocity; + } + + /** + * Perform the actual step + * + * @param nodeId + * @param maxVelocity + * @returns {number} + * @private + */ + _performStep(nodeId,maxVelocity) { + let node = this.body.nodes[nodeId]; + let timestep = this.timestep; + let forces = this.physicsBody.forces; + let velocities = this.physicsBody.velocities; + + // store the state so we can revert + this.previousStates[nodeId] = {x:node.x, y:node.y, vx:velocities[nodeId].x, vy:velocities[nodeId].y}; + + if (node.options.fixed.x === false) { + let dx = this.modelOptions.damping * velocities[nodeId].x; // damping force + let ax = (forces[nodeId].x - dx) / node.options.mass; // acceleration + velocities[nodeId].x += ax * timestep; // velocity + velocities[nodeId].x = (Math.abs(velocities[nodeId].x) > maxVelocity) ? ((velocities[nodeId].x > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].x; + node.x += velocities[nodeId].x * timestep; // position + this.positions[nodeId].x = node.x; + } + else { + forces[nodeId].x = 0; + velocities[nodeId].x = 0; + } + + if (node.options.fixed.y === false) { + let dy = this.modelOptions.damping * velocities[nodeId].y; // damping force + let ay = (forces[nodeId].y - dy) / node.options.mass; // acceleration + velocities[nodeId].y += ay * timestep; // velocity + velocities[nodeId].y = (Math.abs(velocities[nodeId].y) > maxVelocity) ? ((velocities[nodeId].y > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].y; + node.y += velocities[nodeId].y * timestep; // position + this.positions[nodeId].y = node.y; + } + else { + forces[nodeId].y = 0; + velocities[nodeId].y = 0; + } + + let totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x,2) + Math.pow(velocities[nodeId].y,2)); + return totalVelocity; + } + + /** + * calculate the forces for one physics iteration. + */ + calculateForces() { + this.gravitySolver.solve(); + this.nodesSolver.solve(); + this.edgesSolver.solve(); + } } export default PhysicsWorker; From 37e9bcd935b5e0a1dc82e31a4c90991a86993bc0 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Tue, 6 Oct 2015 10:07:38 -0400 Subject: [PATCH 03/17] remove build artifacts --- dist/vis.js | 8998 ++++++++++++++++++------------------ dist/vis.min.css | 2 +- dist/vis.physics.worker.js | 1484 ------ 3 files changed, 4406 insertions(+), 6078 deletions(-) delete mode 100644 dist/vis.physics.worker.js diff --git a/dist/vis.js b/dist/vis.js index 60a695ba..3388fc4b 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -4,8 +4,8 @@ * * A dynamic, browser-based visualization library. * - * @version 4.9.1-SNAPSHOT - * @date 2015-10-05 + * @version 4.9.0 + * @date 2015-10-01 * * @license * Copyright (C) 2011-2015 Almende B.V, http://almende.com @@ -139,10 +139,10 @@ return /******/ (function(modules) { // webpackBootstrap // Network exports.Network = __webpack_require__(59); exports.network = { - Images: __webpack_require__(118), - dotparser: __webpack_require__(116), - gephiParser: __webpack_require__(117), - allOptions: __webpack_require__(112) + Images: __webpack_require__(117), + dotparser: __webpack_require__(115), + gephiParser: __webpack_require__(116), + allOptions: __webpack_require__(111) }; exports.network.convertDot = function (input) { return exports.network.dotparser.DOTToGraph(input); @@ -563,11 +563,11 @@ return /******/ (function(modules) { // webpackBootstrap // object is an ASP date return new Date(Number(match[1])); // parse number } else { - return moment(object).toDate(); // parse string - } - } else { - throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); + return moment(object).toDate(); // parse string } + } else { + throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); + } case 'Moment': if (exports.isNumber(object)) { @@ -584,11 +584,11 @@ return /******/ (function(modules) { // webpackBootstrap // object is an ASP date return moment(Number(match[1])); // parse number } else { - return moment(object); // parse string - } - } else { - throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); + return moment(object); // parse string } + } else { + throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); + } case 'ISODate': if (exports.isNumber(object)) { @@ -603,11 +603,11 @@ return /******/ (function(modules) { // webpackBootstrap // object is an ASP date return new Date(Number(match[1])).toISOString(); // parse number } else { - return new Date(object).toISOString(); // parse string - } - } else { - throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ISODate'); + return new Date(object).toISOString(); // parse string } + } else { + throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ISODate'); + } case 'ASPDate': if (exports.isNumber(object)) { @@ -621,8 +621,8 @@ return /******/ (function(modules) { // webpackBootstrap // object is an ASP date value = new Date(Number(match[1])).valueOf(); // parse number } else { - value = new Date(object).valueOf(); // parse string - } + value = new Date(object).valueOf(); // parse string + } return '/Date(' + value + ')/'; } else { throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ASPDate'); @@ -854,13 +854,13 @@ return /******/ (function(modules) { // webpackBootstrap if (element.addEventListener) { if (useCapture === undefined) useCapture = false; - if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { - action = "DOMMouseScroll"; // For Firefox + if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) { + action = 'DOMMouseScroll'; // For Firefox } element.addEventListener(action, listener, useCapture); } else { - element.attachEvent("on" + action, listener); // IE browsers + element.attachEvent('on' + action, listener); // IE browsers } }; @@ -876,14 +876,14 @@ return /******/ (function(modules) { // webpackBootstrap // non-IE browsers if (useCapture === undefined) useCapture = false; - if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { - action = "DOMMouseScroll"; // For Firefox + if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) { + action = 'DOMMouseScroll'; // For Firefox } element.removeEventListener(action, listener, useCapture); } else { // IE browsers - element.detachEvent("on" + action, listener); + element.detachEvent('on' + action, listener); } }; @@ -896,8 +896,8 @@ return /******/ (function(modules) { // webpackBootstrap if (event.preventDefault) { event.preventDefault(); // non-IE browsers } else { - event.returnValue = false; // IE browsers - } + event.returnValue = false; // IE browsers + } }; /** @@ -1062,17 +1062,17 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} */ exports.overrideOpacity = function (color, opacity) { - if (color.indexOf("rgba") != -1) { + if (color.indexOf('rgba') != -1) { return color; - } else if (color.indexOf("rgb") != -1) { - var rgb = color.substr(color.indexOf("(") + 1).replace(")", "").split(","); - return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + opacity + ")"; + } else if (color.indexOf('rgb') != -1) { + var rgb = color.substr(color.indexOf('(') + 1).replace(')', '').split(','); + return 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',' + opacity + ')'; } else { var rgb = exports.hexToRGB(color); if (rgb == null) { return color; } else { - return "rgba(" + rgb.r + "," + rgb.g + "," + rgb.b + "," + opacity + ")"; + return 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + opacity + ')'; } } }; @@ -1086,7 +1086,7 @@ return /******/ (function(modules) { // webpackBootstrap * @constructor */ exports.RGBToHex = function (red, green, blue) { - return "#" + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1); + return '#' + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1); }; /** @@ -1302,12 +1302,12 @@ return /******/ (function(modules) { // webpackBootstrap }; exports.isValidRGB = function (rgb) { - rgb = rgb.replace(" ", ""); + rgb = rgb.replace(' ', ''); var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb); return isOk; }; exports.isValidRGBA = function (rgba) { - rgba = rgba.replace(" ", ""); + rgba = rgba.replace(' ', ''); var isOk = /rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(.{1,3})\)/i.test(rgba); return isOk; }; @@ -1320,11 +1320,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} */ exports.selectiveBridgeObject = function (fields, referenceObject) { - if (typeof referenceObject == "object") { + if (typeof referenceObject == 'object') { var objectTo = Object.create(referenceObject); for (var i = 0; i < fields.length; i++) { if (referenceObject.hasOwnProperty(fields[i])) { - if (typeof referenceObject[fields[i]] == "object") { + if (typeof referenceObject[fields[i]] == 'object') { objectTo[fields[i]] = exports.bridgeObject(referenceObject[fields[i]]); } } @@ -1343,11 +1343,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} */ exports.bridgeObject = function (referenceObject) { - if (typeof referenceObject == "object") { + if (typeof referenceObject == 'object') { var objectTo = Object.create(referenceObject); for (var i in referenceObject) { if (referenceObject.hasOwnProperty(i)) { - if (typeof referenceObject[i] == "object") { + if (typeof referenceObject[i] == 'object') { objectTo[i] = exports.bridgeObject(referenceObject[i]); } } @@ -4759,16 +4759,16 @@ return /******/ (function(modules) { // webpackBootstrap /* 4 */ /***/ function(module, exports) { - module.exports = function(module) { - if(!module.webpackPolyfill) { - module.deprecate = function() {}; - module.paths = []; - // module.parent = undefined by default - module.children = []; - module.webpackPolyfill = 1; - } - return module; - } + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; + } /***/ }, @@ -5137,21 +5137,21 @@ return /******/ (function(modules) { // webpackBootstrap var point; if (groupTemplate.style == 'circle') { point = exports.getSVGElement('circle', JSONcontainer, svgContainer); - point.setAttributeNS(null, "cx", x); - point.setAttributeNS(null, "cy", y); - point.setAttributeNS(null, "r", 0.5 * groupTemplate.size); + point.setAttributeNS(null, 'cx', x); + point.setAttributeNS(null, 'cy', y); + point.setAttributeNS(null, 'r', 0.5 * groupTemplate.size); } else { point = exports.getSVGElement('rect', JSONcontainer, svgContainer); - point.setAttributeNS(null, "x", x - 0.5 * groupTemplate.size); - point.setAttributeNS(null, "y", y - 0.5 * groupTemplate.size); - point.setAttributeNS(null, "width", groupTemplate.size); - point.setAttributeNS(null, "height", groupTemplate.size); + point.setAttributeNS(null, 'x', x - 0.5 * groupTemplate.size); + point.setAttributeNS(null, 'y', y - 0.5 * groupTemplate.size); + point.setAttributeNS(null, 'width', groupTemplate.size); + point.setAttributeNS(null, 'height', groupTemplate.size); } if (groupTemplate.styles !== undefined) { - point.setAttributeNS(null, "style", groupTemplate.styles); + point.setAttributeNS(null, 'style', groupTemplate.styles); } - point.setAttributeNS(null, "class", groupTemplate.className + " vis-point"); + point.setAttributeNS(null, 'class', groupTemplate.className + ' vis-point'); //handle label if (labelObj) { @@ -5168,10 +5168,10 @@ return /******/ (function(modules) { // webpackBootstrap } if (labelObj.className) { - label.setAttributeNS(null, "class", labelObj.className + " vis-label"); + label.setAttributeNS(null, 'class', labelObj.className + ' vis-label'); } - label.setAttributeNS(null, "x", x); - label.setAttributeNS(null, "y", y); + label.setAttributeNS(null, 'x', x); + label.setAttributeNS(null, 'y', y); } return point; @@ -5191,13 +5191,13 @@ return /******/ (function(modules) { // webpackBootstrap y -= height; } var rect = exports.getSVGElement('rect', JSONcontainer, svgContainer); - rect.setAttributeNS(null, "x", x - 0.5 * width); - rect.setAttributeNS(null, "y", y); - rect.setAttributeNS(null, "width", width); - rect.setAttributeNS(null, "height", height); - rect.setAttributeNS(null, "class", className); + rect.setAttributeNS(null, 'x', x - 0.5 * width); + rect.setAttributeNS(null, 'y', y); + rect.setAttributeNS(null, 'width', width); + rect.setAttributeNS(null, 'height', height); + rect.setAttributeNS(null, 'class', className); if (style) { - rect.setAttributeNS(null, "style", style); + rect.setAttributeNS(null, 'style', style); } } }; @@ -5827,8 +5827,8 @@ return /******/ (function(modules) { // webpackBootstrap // TODO: extend order by an Object {field:String, direction:String} // where direction can be 'asc' or 'desc' else { - throw new TypeError('Order must be a function or a string'); - } + throw new TypeError('Order must be a function or a string'); + } }; /** @@ -6604,9 +6604,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this._ids[id]) { delete this._ids[id]; removed.push(id); - } else { - // nothing interesting for me :-( - } + } else {} } } @@ -6650,6 +6648,8 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = DataView; + // nothing interesting for me :-( + /***/ }, /* 11 */ /***/ function(module, exports, __webpack_require__) { @@ -6893,11 +6893,9 @@ return /******/ (function(modules) { // webpackBootstrap if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; - } else if (backgroundColor === undefined) { - // use use defaults - } else { - throw 'Unsupported type of backgroundColor'; - } + } else if (backgroundColor === undefined) {} else { + throw 'Unsupported type of backgroundColor'; + } this.frame.style.backgroundColor = fill; this.frame.style.borderColor = stroke; @@ -7603,9 +7601,9 @@ return /******/ (function(modules) { // webpackBootstrap widthMin = dotSize / 2; // px widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function } else { - widthMin = 20; // px - widthMax = 20; // px - } + widthMin = 20; // px + widthMax = 20; // px + } var height = Math.max(this.frame.clientHeight * 0.25, 100); var top = this.margin; @@ -8141,9 +8139,9 @@ return /******/ (function(modules) { // webpackBootstrap strokeStyle = this.axisColor; // TODO: should be customizable } } else { - fillStyle = 'gray'; - strokeStyle = this.axisColor; - } + fillStyle = 'gray'; + strokeStyle = this.axisColor; + } ctx.lineWidth = this._getStrokeWidth(point); ctx.fillStyle = fillStyle; @@ -8159,39 +8157,39 @@ return /******/ (function(modules) { // webpackBootstrap } } } else { - // grid style - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; + // grid style + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; - if (point !== undefined && right !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + if (point !== undefined && right !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - ctx.lineWidth = this._getStrokeWidth(point) * 2; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.stroke(); - } + ctx.lineWidth = this._getStrokeWidth(point) * 2; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.stroke(); + } - if (point !== undefined && top !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + top.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + if (point !== undefined && top !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + top.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - ctx.lineWidth = this._getStrokeWidth(point) * 2; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.stroke(); - } + ctx.lineWidth = this._getStrokeWidth(point) * 2; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.stroke(); } } + } }; Graph3d.prototype._getStrokeWidth = function (point) { @@ -8898,6 +8896,8 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Graph3d; + // use use defaults + /***/ }, /* 12 */ /***/ function(module, exports) { @@ -10416,10 +10416,7 @@ return /******/ (function(modules) { // webpackBootstrap var getEnd = function getEnd(item) { var end = item.data.end != undefined ? item.data.end : item.data.start; return util.convert(end, 'Date').valueOf(); - } - - // calculate the date of the left side and right side of the items given - ; + }; interval = max - min; // ms @@ -10427,6 +10424,8 @@ return /******/ (function(modules) { // webpackBootstrap interval = 10; } factor = interval / _this.props.center.width; + + // calculate the date of the left side and right side of the items given util.forEach(_this.itemSet.items, (function (item) { item.show(); @@ -10795,2893 +10794,2893 @@ return /******/ (function(modules) { // webpackBootstrap /* 22 */ /***/ function(module, exports, __webpack_require__) { - var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.4 - 2014-09-28 - * http://hammerjs.github.io/ - * - * Copyright (c) 2014 Jorik Tangelder; - * Licensed under the MIT license */ - (function(window, document, exportName, undefined) { - 'use strict'; - - var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o']; - var TEST_ELEMENT = document.createElement('div'); - - var TYPE_FUNCTION = 'function'; - - var round = Math.round; - var abs = Math.abs; - var now = Date.now; - - /** - * set a timeout with a given scope - * @param {Function} fn - * @param {Number} timeout - * @param {Object} context - * @returns {number} - */ - function setTimeoutContext(fn, timeout, context) { - return setTimeout(bindFn(fn, context), timeout); - } - - /** - * if the argument is an array, we want to execute the fn on each entry - * if it aint an array we don't want to do a thing. - * this is used by all the methods that accept a single and array argument. - * @param {*|Array} arg - * @param {String} fn - * @param {Object} [context] - * @returns {Boolean} - */ - function invokeArrayArg(arg, fn, context) { - if (Array.isArray(arg)) { - each(arg, context[fn], context); - return true; - } - return false; - } - - /** - * walk objects and arrays - * @param {Object} obj - * @param {Function} iterator - * @param {Object} context - */ - function each(obj, iterator, context) { - var i; - - if (!obj) { - return; - } - - if (obj.forEach) { - obj.forEach(iterator, context); - } else if (obj.length !== undefined) { - i = 0; - while (i < obj.length) { - iterator.call(context, obj[i], i, obj); - i++; - } - } else { - for (i in obj) { - obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); - } - } - } - - /** - * extend object. - * means that properties in dest will be overwritten by the ones in src. - * @param {Object} dest - * @param {Object} src - * @param {Boolean} [merge] - * @returns {Object} dest - */ - function extend(dest, src, merge) { - var keys = Object.keys(src); - var i = 0; - while (i < keys.length) { - if (!merge || (merge && dest[keys[i]] === undefined)) { - dest[keys[i]] = src[keys[i]]; - } - i++; - } - return dest; - } - - /** - * merge the values from src in the dest. - * means that properties that exist in dest will not be overwritten by src - * @param {Object} dest - * @param {Object} src - * @returns {Object} dest - */ - function merge(dest, src) { - return extend(dest, src, true); - } - - /** - * simple class inheritance - * @param {Function} child - * @param {Function} base - * @param {Object} [properties] - */ - function inherit(child, base, properties) { - var baseP = base.prototype, - childP; - - childP = child.prototype = Object.create(baseP); - childP.constructor = child; - childP._super = baseP; - - if (properties) { - extend(childP, properties); - } - } - - /** - * simple function bind - * @param {Function} fn - * @param {Object} context - * @returns {Function} - */ - function bindFn(fn, context) { - return function boundFn() { - return fn.apply(context, arguments); - }; - } - - /** - * let a boolean value also be a function that must return a boolean - * this first item in args will be used as the context - * @param {Boolean|Function} val - * @param {Array} [args] - * @returns {Boolean} - */ - function boolOrFn(val, args) { - if (typeof val == TYPE_FUNCTION) { - return val.apply(args ? args[0] || undefined : undefined, args); - } - return val; - } - - /** - * use the val2 when val1 is undefined - * @param {*} val1 - * @param {*} val2 - * @returns {*} - */ - function ifUndefined(val1, val2) { - return (val1 === undefined) ? val2 : val1; - } - - /** - * addEventListener with multiple events at once - * @param {EventTarget} target - * @param {String} types - * @param {Function} handler - */ - function addEventListeners(target, types, handler) { - each(splitStr(types), function(type) { - target.addEventListener(type, handler, false); - }); - } - - /** - * removeEventListener with multiple events at once - * @param {EventTarget} target - * @param {String} types - * @param {Function} handler - */ - function removeEventListeners(target, types, handler) { - each(splitStr(types), function(type) { - target.removeEventListener(type, handler, false); - }); - } - - /** - * find if a node is in the given parent - * @method hasParent - * @param {HTMLElement} node - * @param {HTMLElement} parent - * @return {Boolean} found - */ - function hasParent(node, parent) { - while (node) { - if (node == parent) { - return true; - } - node = node.parentNode; - } - return false; - } - - /** - * small indexOf wrapper - * @param {String} str - * @param {String} find - * @returns {Boolean} found - */ - function inStr(str, find) { - return str.indexOf(find) > -1; - } - - /** - * split string on whitespace - * @param {String} str - * @returns {Array} words - */ - function splitStr(str) { - return str.trim().split(/\s+/g); - } - - /** - * find if a array contains the object using indexOf or a simple polyFill - * @param {Array} src - * @param {String} find - * @param {String} [findByKey] - * @return {Boolean|Number} false when not found, or the index - */ - function inArray(src, find, findByKey) { - if (src.indexOf && !findByKey) { - return src.indexOf(find); - } else { - var i = 0; - while (i < src.length) { - if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { - return i; - } - i++; - } - return -1; - } - } - - /** - * convert array-like objects to real arrays - * @param {Object} obj - * @returns {Array} - */ - function toArray(obj) { - return Array.prototype.slice.call(obj, 0); - } - - /** - * unique array with objects based on a key (like 'id') or just by the array's value - * @param {Array} src [{id:1},{id:2},{id:1}] - * @param {String} [key] - * @param {Boolean} [sort=False] - * @returns {Array} [{id:1},{id:2}] - */ - function uniqueArray(src, key, sort) { - var results = []; - var values = []; - var i = 0; - - while (i < src.length) { - var val = key ? src[i][key] : src[i]; - if (inArray(values, val) < 0) { - results.push(src[i]); - } - values[i] = val; - i++; - } - - if (sort) { - if (!key) { - results = results.sort(); - } else { - results = results.sort(function sortUniqueArray(a, b) { - return a[key] > b[key]; - }); - } - } - - return results; - } - - /** - * get the prefixed property - * @param {Object} obj - * @param {String} property - * @returns {String|Undefined} prefixed - */ - function prefixed(obj, property) { - var prefix, prop; - var camelProp = property[0].toUpperCase() + property.slice(1); - - var i = 0; - while (i < VENDOR_PREFIXES.length) { - prefix = VENDOR_PREFIXES[i]; - prop = (prefix) ? prefix + camelProp : property; - - if (prop in obj) { - return prop; - } - i++; - } - return undefined; - } - - /** - * get a unique id - * @returns {number} uniqueId - */ - var _uniqueId = 1; - function uniqueId() { - return _uniqueId++; - } - - /** - * get the window object of an element - * @param {HTMLElement} element - * @returns {DocumentView|Window} - */ - function getWindowForElement(element) { - var doc = element.ownerDocument; - return (doc.defaultView || doc.parentWindow); - } - - var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; - - var SUPPORT_TOUCH = ('ontouchstart' in window); - var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; - var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); - - var INPUT_TYPE_TOUCH = 'touch'; - var INPUT_TYPE_PEN = 'pen'; - var INPUT_TYPE_MOUSE = 'mouse'; - var INPUT_TYPE_KINECT = 'kinect'; - - var COMPUTE_INTERVAL = 25; - - var INPUT_START = 1; - var INPUT_MOVE = 2; - var INPUT_END = 4; - var INPUT_CANCEL = 8; - - var DIRECTION_NONE = 1; - var DIRECTION_LEFT = 2; - var DIRECTION_RIGHT = 4; - var DIRECTION_UP = 8; - var DIRECTION_DOWN = 16; - - var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; - var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; - var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; - - var PROPS_XY = ['x', 'y']; - var PROPS_CLIENT_XY = ['clientX', 'clientY']; - - /** - * create new input type manager - * @param {Manager} manager - * @param {Function} callback - * @returns {Input} - * @constructor - */ - function Input(manager, callback) { - var self = this; - this.manager = manager; - this.callback = callback; - this.element = manager.element; - this.target = manager.options.inputTarget; - - // smaller wrapper around the handler, for the scope and the enabled state of the manager, - // so when disabled the input events are completely bypassed. - this.domHandler = function(ev) { - if (boolOrFn(manager.options.enable, [manager])) { - self.handler(ev); - } - }; - - this.init(); - - } - - Input.prototype = { - /** - * should handle the inputEvent data and trigger the callback - * @virtual - */ - handler: function() { }, - - /** - * bind the events - */ - init: function() { - this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); - this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); - this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); - }, - - /** - * unbind the events - */ - destroy: function() { - this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); - this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); - this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); - } - }; - - /** - * create new input type manager - * called by the Manager constructor - * @param {Hammer} manager - * @returns {Input} - */ - function createInputInstance(manager) { - var Type; - var inputClass = manager.options.inputClass; - - if (inputClass) { - Type = inputClass; - } else if (SUPPORT_POINTER_EVENTS) { - Type = PointerEventInput; - } else if (SUPPORT_ONLY_TOUCH) { - Type = TouchInput; - } else if (!SUPPORT_TOUCH) { - Type = MouseInput; - } else { - Type = TouchMouseInput; - } - return new (Type)(manager, inputHandler); - } - - /** - * handle input events - * @param {Manager} manager - * @param {String} eventType - * @param {Object} input - */ - function inputHandler(manager, eventType, input) { - var pointersLen = input.pointers.length; - var changedPointersLen = input.changedPointers.length; - var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); - var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); - - input.isFirst = !!isFirst; - input.isFinal = !!isFinal; - - if (isFirst) { - manager.session = {}; - } - - // source event is the normalized value of the domEvents - // like 'touchstart, mouseup, pointerdown' - input.eventType = eventType; - - // compute scale, rotation etc - computeInputData(manager, input); - - // emit secret event - manager.emit('hammer.input', input); - - manager.recognize(input); - manager.session.prevInput = input; - } - - /** - * extend the data with some usable properties like scale, rotate, velocity etc - * @param {Object} manager - * @param {Object} input - */ - function computeInputData(manager, input) { - var session = manager.session; - var pointers = input.pointers; - var pointersLength = pointers.length; - - // store the first input to calculate the distance and direction - if (!session.firstInput) { - session.firstInput = simpleCloneInputData(input); - } - - // to compute scale and rotation we need to store the multiple touches - if (pointersLength > 1 && !session.firstMultiple) { - session.firstMultiple = simpleCloneInputData(input); - } else if (pointersLength === 1) { - session.firstMultiple = false; - } - - var firstInput = session.firstInput; - var firstMultiple = session.firstMultiple; - var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; - - var center = input.center = getCenter(pointers); - input.timeStamp = now(); - input.deltaTime = input.timeStamp - firstInput.timeStamp; - - input.angle = getAngle(offsetCenter, center); - input.distance = getDistance(offsetCenter, center); - - computeDeltaXY(session, input); - input.offsetDirection = getDirection(input.deltaX, input.deltaY); - - input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; - input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; - - computeIntervalInputData(session, input); - - // find the correct target - var target = manager.element; - if (hasParent(input.srcEvent.target, target)) { - target = input.srcEvent.target; - } - input.target = target; - } - - function computeDeltaXY(session, input) { - var center = input.center; - var offset = session.offsetDelta || {}; - var prevDelta = session.prevDelta || {}; - var prevInput = session.prevInput || {}; - - if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { - prevDelta = session.prevDelta = { - x: prevInput.deltaX || 0, - y: prevInput.deltaY || 0 - }; - - offset = session.offsetDelta = { - x: center.x, - y: center.y - }; - } - - input.deltaX = prevDelta.x + (center.x - offset.x); - input.deltaY = prevDelta.y + (center.y - offset.y); - } - - /** - * velocity is calculated every x ms - * @param {Object} session - * @param {Object} input - */ - function computeIntervalInputData(session, input) { - var last = session.lastInterval || input, - deltaTime = input.timeStamp - last.timeStamp, - velocity, velocityX, velocityY, direction; - - if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { - var deltaX = last.deltaX - input.deltaX; - var deltaY = last.deltaY - input.deltaY; - - var v = getVelocity(deltaTime, deltaX, deltaY); - velocityX = v.x; - velocityY = v.y; - velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; - direction = getDirection(deltaX, deltaY); - - session.lastInterval = input; - } else { - // use latest velocity info if it doesn't overtake a minimum period - velocity = last.velocity; - velocityX = last.velocityX; - velocityY = last.velocityY; - direction = last.direction; - } - - input.velocity = velocity; - input.velocityX = velocityX; - input.velocityY = velocityY; - input.direction = direction; - } - - /** - * create a simple clone from the input used for storage of firstInput and firstMultiple - * @param {Object} input - * @returns {Object} clonedInputData - */ - function simpleCloneInputData(input) { - // make a simple copy of the pointers because we will get a reference if we don't - // we only need clientXY for the calculations - var pointers = []; - var i = 0; - while (i < input.pointers.length) { - pointers[i] = { - clientX: round(input.pointers[i].clientX), - clientY: round(input.pointers[i].clientY) - }; - i++; - } - - return { - timeStamp: now(), - pointers: pointers, - center: getCenter(pointers), - deltaX: input.deltaX, - deltaY: input.deltaY - }; - } - - /** - * get the center of all the pointers - * @param {Array} pointers - * @return {Object} center contains `x` and `y` properties - */ - function getCenter(pointers) { - var pointersLength = pointers.length; - - // no need to loop when only one touch - if (pointersLength === 1) { - return { - x: round(pointers[0].clientX), - y: round(pointers[0].clientY) - }; - } - - var x = 0, y = 0, i = 0; - while (i < pointersLength) { - x += pointers[i].clientX; - y += pointers[i].clientY; - i++; - } - - return { - x: round(x / pointersLength), - y: round(y / pointersLength) - }; - } - - /** - * calculate the velocity between two points. unit is in px per ms. - * @param {Number} deltaTime - * @param {Number} x - * @param {Number} y - * @return {Object} velocity `x` and `y` - */ - function getVelocity(deltaTime, x, y) { - return { - x: x / deltaTime || 0, - y: y / deltaTime || 0 - }; - } - - /** - * get the direction between two points - * @param {Number} x - * @param {Number} y - * @return {Number} direction - */ - function getDirection(x, y) { - if (x === y) { - return DIRECTION_NONE; - } - - if (abs(x) >= abs(y)) { - return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - return y > 0 ? DIRECTION_UP : DIRECTION_DOWN; - } - - /** - * calculate the absolute distance between two points - * @param {Object} p1 {x, y} - * @param {Object} p2 {x, y} - * @param {Array} [props] containing x and y keys - * @return {Number} distance - */ - function getDistance(p1, p2, props) { - if (!props) { - props = PROPS_XY; - } - var x = p2[props[0]] - p1[props[0]], - y = p2[props[1]] - p1[props[1]]; - - return Math.sqrt((x * x) + (y * y)); - } - - /** - * calculate the angle between two coordinates - * @param {Object} p1 - * @param {Object} p2 - * @param {Array} [props] containing x and y keys - * @return {Number} angle - */ - function getAngle(p1, p2, props) { - if (!props) { - props = PROPS_XY; - } - var x = p2[props[0]] - p1[props[0]], - y = p2[props[1]] - p1[props[1]]; - return Math.atan2(y, x) * 180 / Math.PI; - } - - /** - * calculate the rotation degrees between two pointersets - * @param {Array} start array of pointers - * @param {Array} end array of pointers - * @return {Number} rotation - */ - function getRotation(start, end) { - return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY); - } - - /** - * calculate the scale factor between two pointersets - * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out - * @param {Array} start array of pointers - * @param {Array} end array of pointers - * @return {Number} scale - */ - function getScale(start, end) { - return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); - } - - var MOUSE_INPUT_MAP = { - mousedown: INPUT_START, - mousemove: INPUT_MOVE, - mouseup: INPUT_END - }; - - var MOUSE_ELEMENT_EVENTS = 'mousedown'; - var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; - - /** - * Mouse events input - * @constructor - * @extends Input - */ - function MouseInput() { - this.evEl = MOUSE_ELEMENT_EVENTS; - this.evWin = MOUSE_WINDOW_EVENTS; - - this.allow = true; // used by Input.TouchMouse to disable mouse events - this.pressed = false; // mousedown state - - Input.apply(this, arguments); - } - - inherit(MouseInput, Input, { - /** - * handle mouse events - * @param {Object} ev - */ - handler: function MEhandler(ev) { - var eventType = MOUSE_INPUT_MAP[ev.type]; - - // on start we want to have the left mouse button down - if (eventType & INPUT_START && ev.button === 0) { - this.pressed = true; - } - - if (eventType & INPUT_MOVE && ev.which !== 1) { - eventType = INPUT_END; - } - - // mouse must be down, and mouse events are allowed (see the TouchMouse input) - if (!this.pressed || !this.allow) { - return; - } - - if (eventType & INPUT_END) { - this.pressed = false; - } - - this.callback(this.manager, eventType, { - pointers: [ev], - changedPointers: [ev], - pointerType: INPUT_TYPE_MOUSE, - srcEvent: ev - }); - } - }); - - var POINTER_INPUT_MAP = { - pointerdown: INPUT_START, - pointermove: INPUT_MOVE, - pointerup: INPUT_END, - pointercancel: INPUT_CANCEL, - pointerout: INPUT_CANCEL - }; - - // in IE10 the pointer types is defined as an enum - var IE10_POINTER_TYPE_ENUM = { - 2: INPUT_TYPE_TOUCH, - 3: INPUT_TYPE_PEN, - 4: INPUT_TYPE_MOUSE, - 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 - }; - - var POINTER_ELEMENT_EVENTS = 'pointerdown'; - var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; - - // IE10 has prefixed support, and case-sensitive - if (window.MSPointerEvent) { - POINTER_ELEMENT_EVENTS = 'MSPointerDown'; - POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; - } - - /** - * Pointer events input - * @constructor - * @extends Input - */ - function PointerEventInput() { - this.evEl = POINTER_ELEMENT_EVENTS; - this.evWin = POINTER_WINDOW_EVENTS; - - Input.apply(this, arguments); - - this.store = (this.manager.session.pointerEvents = []); - } - - inherit(PointerEventInput, Input, { - /** - * handle mouse events - * @param {Object} ev - */ - handler: function PEhandler(ev) { - var store = this.store; - var removePointer = false; - - var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); - var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; - var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; - - var isTouch = (pointerType == INPUT_TYPE_TOUCH); - - // get index of the event in the store - var storeIndex = inArray(store, ev.pointerId, 'pointerId'); - - // start and mouse must be down - if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { - if (storeIndex < 0) { - store.push(ev); - storeIndex = store.length - 1; - } - } else if (eventType & (INPUT_END | INPUT_CANCEL)) { - removePointer = true; - } - - // it not found, so the pointer hasn't been down (so it's probably a hover) - if (storeIndex < 0) { - return; - } - - // update the event in the store - store[storeIndex] = ev; - - this.callback(this.manager, eventType, { - pointers: store, - changedPointers: [ev], - pointerType: pointerType, - srcEvent: ev - }); - - if (removePointer) { - // remove from the store - store.splice(storeIndex, 1); - } - } - }); - - var SINGLE_TOUCH_INPUT_MAP = { - touchstart: INPUT_START, - touchmove: INPUT_MOVE, - touchend: INPUT_END, - touchcancel: INPUT_CANCEL - }; - - var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; - var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; - - /** - * Touch events input - * @constructor - * @extends Input - */ - function SingleTouchInput() { - this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; - this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; - this.started = false; - - Input.apply(this, arguments); - } - - inherit(SingleTouchInput, Input, { - handler: function TEhandler(ev) { - var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; - - // should we handle the touch events? - if (type === INPUT_START) { - this.started = true; - } - - if (!this.started) { - return; - } - - var touches = normalizeSingleTouches.call(this, ev, type); - - // when done, reset the started state - if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { - this.started = false; - } - - this.callback(this.manager, type, { - pointers: touches[0], - changedPointers: touches[1], - pointerType: INPUT_TYPE_TOUCH, - srcEvent: ev - }); - } - }); - - /** - * @this {TouchInput} - * @param {Object} ev - * @param {Number} type flag - * @returns {undefined|Array} [all, changed] - */ - function normalizeSingleTouches(ev, type) { - var all = toArray(ev.touches); - var changed = toArray(ev.changedTouches); - - if (type & (INPUT_END | INPUT_CANCEL)) { - all = uniqueArray(all.concat(changed), 'identifier', true); - } - - return [all, changed]; - } - - var TOUCH_INPUT_MAP = { - touchstart: INPUT_START, - touchmove: INPUT_MOVE, - touchend: INPUT_END, - touchcancel: INPUT_CANCEL - }; - - var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; - - /** - * Multi-user touch events input - * @constructor - * @extends Input - */ - function TouchInput() { - this.evTarget = TOUCH_TARGET_EVENTS; - this.targetIds = {}; - - Input.apply(this, arguments); - } - - inherit(TouchInput, Input, { - handler: function MTEhandler(ev) { - var type = TOUCH_INPUT_MAP[ev.type]; - var touches = getTouches.call(this, ev, type); - if (!touches) { - return; - } - - this.callback(this.manager, type, { - pointers: touches[0], - changedPointers: touches[1], - pointerType: INPUT_TYPE_TOUCH, - srcEvent: ev - }); - } - }); - - /** - * @this {TouchInput} - * @param {Object} ev - * @param {Number} type flag - * @returns {undefined|Array} [all, changed] - */ - function getTouches(ev, type) { - var allTouches = toArray(ev.touches); - var targetIds = this.targetIds; - - // when there is only one touch, the process can be simplified - if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { - targetIds[allTouches[0].identifier] = true; - return [allTouches, allTouches]; - } - - var i, - targetTouches, - changedTouches = toArray(ev.changedTouches), - changedTargetTouches = [], - target = this.target; - - // get target touches from touches - targetTouches = allTouches.filter(function(touch) { - return hasParent(touch.target, target); - }); - - // collect touches - if (type === INPUT_START) { - i = 0; - while (i < targetTouches.length) { - targetIds[targetTouches[i].identifier] = true; - i++; - } - } - - // filter changed touches to only contain touches that exist in the collected target ids - i = 0; - while (i < changedTouches.length) { - if (targetIds[changedTouches[i].identifier]) { - changedTargetTouches.push(changedTouches[i]); - } - - // cleanup removed touches - if (type & (INPUT_END | INPUT_CANCEL)) { - delete targetIds[changedTouches[i].identifier]; - } - i++; - } - - if (!changedTargetTouches.length) { - return; - } - - return [ - // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' - uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), - changedTargetTouches - ]; - } - - /** - * Combined touch and mouse input - * - * Touch has a higher priority then mouse, and while touching no mouse events are allowed. - * This because touch devices also emit mouse events while doing a touch. - * - * @constructor - * @extends Input - */ - function TouchMouseInput() { - Input.apply(this, arguments); - - var handler = bindFn(this.handler, this); - this.touch = new TouchInput(this.manager, handler); - this.mouse = new MouseInput(this.manager, handler); - } - - inherit(TouchMouseInput, Input, { - /** - * handle mouse and touch events - * @param {Hammer} manager - * @param {String} inputEvent - * @param {Object} inputData - */ - handler: function TMEhandler(manager, inputEvent, inputData) { - var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), - isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); - - // when we're in a touch event, so block all upcoming mouse events - // most mobile browser also emit mouseevents, right after touchstart - if (isTouch) { - this.mouse.allow = false; - } else if (isMouse && !this.mouse.allow) { - return; - } - - // reset the allowMouse when we're done - if (inputEvent & (INPUT_END | INPUT_CANCEL)) { - this.mouse.allow = true; - } - - this.callback(manager, inputEvent, inputData); - }, - - /** - * remove the event listeners - */ - destroy: function destroy() { - this.touch.destroy(); - this.mouse.destroy(); - } - }); - - var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); - var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; - - // magical touchAction value - var TOUCH_ACTION_COMPUTE = 'compute'; - var TOUCH_ACTION_AUTO = 'auto'; - var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented - var TOUCH_ACTION_NONE = 'none'; - var TOUCH_ACTION_PAN_X = 'pan-x'; - var TOUCH_ACTION_PAN_Y = 'pan-y'; - - /** - * Touch Action - * sets the touchAction property or uses the js alternative - * @param {Manager} manager - * @param {String} value - * @constructor - */ - function TouchAction(manager, value) { - this.manager = manager; - this.set(value); - } - - TouchAction.prototype = { - /** - * set the touchAction value on the element or enable the polyfill - * @param {String} value - */ - set: function(value) { - // find out the touch-action by the event handlers - if (value == TOUCH_ACTION_COMPUTE) { - value = this.compute(); - } - - if (NATIVE_TOUCH_ACTION) { - this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; - } - this.actions = value.toLowerCase().trim(); - }, - - /** - * just re-set the touchAction value - */ - update: function() { - this.set(this.manager.options.touchAction); - }, - - /** - * compute the value for the touchAction property based on the recognizer's settings - * @returns {String} value - */ - compute: function() { - var actions = []; - each(this.manager.recognizers, function(recognizer) { - if (boolOrFn(recognizer.options.enable, [recognizer])) { - actions = actions.concat(recognizer.getTouchAction()); - } - }); - return cleanTouchActions(actions.join(' ')); - }, - - /** - * this method is called on each input cycle and provides the preventing of the browser behavior - * @param {Object} input - */ - preventDefaults: function(input) { - // not needed with native support for the touchAction property - if (NATIVE_TOUCH_ACTION) { - return; - } - - var srcEvent = input.srcEvent; - var direction = input.offsetDirection; - - // if the touch action did prevented once this session - if (this.manager.session.prevented) { - srcEvent.preventDefault(); - return; - } - - var actions = this.actions; - var hasNone = inStr(actions, TOUCH_ACTION_NONE); - var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); - var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); - - if (hasNone || - (hasPanY && direction & DIRECTION_HORIZONTAL) || - (hasPanX && direction & DIRECTION_VERTICAL)) { - return this.preventSrc(srcEvent); - } - }, - - /** - * call preventDefault to prevent the browser's default behavior (scrolling in most cases) - * @param {Object} srcEvent - */ - preventSrc: function(srcEvent) { - this.manager.session.prevented = true; - srcEvent.preventDefault(); - } - }; - - /** - * when the touchActions are collected they are not a valid value, so we need to clean things up. * - * @param {String} actions - * @returns {*} - */ - function cleanTouchActions(actions) { - // none - if (inStr(actions, TOUCH_ACTION_NONE)) { - return TOUCH_ACTION_NONE; - } - - var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); - var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); - - // pan-x and pan-y can be combined - if (hasPanX && hasPanY) { - return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y; - } - - // pan-x OR pan-y - if (hasPanX || hasPanY) { - return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; - } - - // manipulation - if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { - return TOUCH_ACTION_MANIPULATION; - } - - return TOUCH_ACTION_AUTO; - } - - /** - * Recognizer flow explained; * - * All recognizers have the initial state of POSSIBLE when a input session starts. - * The definition of a input session is from the first input until the last input, with all it's movement in it. * - * Example session for mouse-input: mousedown -> mousemove -> mouseup - * - * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed - * which determines with state it should be. - * - * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to - * POSSIBLE to give it another change on the next cycle. - * - * Possible - * | - * +-----+---------------+ - * | | - * +-----+-----+ | - * | | | - * Failed Cancelled | - * +-------+------+ - * | | - * Recognized Began - * | - * Changed - * | - * Ended/Recognized - */ - var STATE_POSSIBLE = 1; - var STATE_BEGAN = 2; - var STATE_CHANGED = 4; - var STATE_ENDED = 8; - var STATE_RECOGNIZED = STATE_ENDED; - var STATE_CANCELLED = 16; - var STATE_FAILED = 32; - - /** - * Recognizer - * Every recognizer needs to extend from this class. - * @constructor - * @param {Object} options - */ - function Recognizer(options) { - this.id = uniqueId(); - - this.manager = null; - this.options = merge(options || {}, this.defaults); - - // default is enable true - this.options.enable = ifUndefined(this.options.enable, true); - - this.state = STATE_POSSIBLE; - - this.simultaneous = {}; - this.requireFail = []; - } - - Recognizer.prototype = { - /** - * @virtual - * @type {Object} - */ - defaults: {}, - - /** - * set options - * @param {Object} options - * @return {Recognizer} - */ - set: function(options) { - extend(this.options, options); - - // also update the touchAction, in case something changed about the directions/enabled state - this.manager && this.manager.touchAction.update(); - return this; - }, - - /** - * recognize simultaneous with an other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - recognizeWith: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { - return this; - } - - var simultaneous = this.simultaneous; - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - if (!simultaneous[otherRecognizer.id]) { - simultaneous[otherRecognizer.id] = otherRecognizer; - otherRecognizer.recognizeWith(this); - } - return this; - }, - - /** - * drop the simultaneous link. it doesnt remove the link on the other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - dropRecognizeWith: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { - return this; - } - - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - delete this.simultaneous[otherRecognizer.id]; - return this; - }, - - /** - * recognizer can only run when an other is failing - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - requireFailure: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { - return this; - } - - var requireFail = this.requireFail; - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - if (inArray(requireFail, otherRecognizer) === -1) { - requireFail.push(otherRecognizer); - otherRecognizer.requireFailure(this); - } - return this; - }, - - /** - * drop the requireFailure link. it does not remove the link on the other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - dropRequireFailure: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { - return this; - } - - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - var index = inArray(this.requireFail, otherRecognizer); - if (index > -1) { - this.requireFail.splice(index, 1); - } - return this; - }, - - /** - * has require failures boolean - * @returns {boolean} - */ - hasRequireFailures: function() { - return this.requireFail.length > 0; - }, - - /** - * if the recognizer can recognize simultaneous with an other recognizer - * @param {Recognizer} otherRecognizer - * @returns {Boolean} - */ - canRecognizeWith: function(otherRecognizer) { - return !!this.simultaneous[otherRecognizer.id]; - }, - - /** - * You should use `tryEmit` instead of `emit` directly to check - * that all the needed recognizers has failed before emitting. - * @param {Object} input - */ - emit: function(input) { - var self = this; - var state = this.state; - - function emit(withState) { - self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input); - } - - // 'panstart' and 'panmove' - if (state < STATE_ENDED) { - emit(true); - } - - emit(); // simple 'eventName' events - - // panend and pancancel - if (state >= STATE_ENDED) { - emit(true); - } - }, - - /** - * Check that all the require failure recognizers has failed, - * if true, it emits a gesture event, - * otherwise, setup the state to FAILED. - * @param {Object} input - */ - tryEmit: function(input) { - if (this.canEmit()) { - return this.emit(input); - } - // it's failing anyway - this.state = STATE_FAILED; - }, - - /** - * can we emit? - * @returns {boolean} - */ - canEmit: function() { - var i = 0; - while (i < this.requireFail.length) { - if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { - return false; - } - i++; - } - return true; - }, - - /** - * update the recognizer - * @param {Object} inputData - */ - recognize: function(inputData) { - // make a new copy of the inputData - // so we can change the inputData without messing up the other recognizers - var inputDataClone = extend({}, inputData); - - // is is enabled and allow recognizing? - if (!boolOrFn(this.options.enable, [this, inputDataClone])) { - this.reset(); - this.state = STATE_FAILED; - return; - } - - // reset when we've reached the end - if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { - this.state = STATE_POSSIBLE; - } - - this.state = this.process(inputDataClone); - - // the recognizer has recognized a gesture - // so trigger an event - if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { - this.tryEmit(inputDataClone); - } - }, - - /** - * return the state of the recognizer - * the actual recognizing happens in this method - * @virtual - * @param {Object} inputData - * @returns {Const} STATE - */ - process: function(inputData) { }, // jshint ignore:line - - /** - * return the preferred touch-action - * @virtual - * @returns {Array} - */ - getTouchAction: function() { }, - - /** - * called when the gesture isn't allowed to recognize - * like when another is being recognized or it is disabled - * @virtual - */ - reset: function() { } - }; - - /** - * get a usable string, used as event postfix - * @param {Const} state - * @returns {String} state - */ - function stateStr(state) { - if (state & STATE_CANCELLED) { - return 'cancel'; - } else if (state & STATE_ENDED) { - return 'end'; - } else if (state & STATE_CHANGED) { - return 'move'; - } else if (state & STATE_BEGAN) { - return 'start'; - } - return ''; - } - - /** - * direction cons to string - * @param {Const} direction - * @returns {String} - */ - function directionStr(direction) { - if (direction == DIRECTION_DOWN) { - return 'down'; - } else if (direction == DIRECTION_UP) { - return 'up'; - } else if (direction == DIRECTION_LEFT) { - return 'left'; - } else if (direction == DIRECTION_RIGHT) { - return 'right'; - } - return ''; - } - - /** - * get a recognizer by name if it is bound to a manager - * @param {Recognizer|String} otherRecognizer - * @param {Recognizer} recognizer - * @returns {Recognizer} - */ - function getRecognizerByNameIfManager(otherRecognizer, recognizer) { - var manager = recognizer.manager; - if (manager) { - return manager.get(otherRecognizer); - } - return otherRecognizer; - } - - /** - * This recognizer is just used as a base for the simple attribute recognizers. - * @constructor - * @extends Recognizer - */ - function AttrRecognizer() { - Recognizer.apply(this, arguments); - } - - inherit(AttrRecognizer, Recognizer, { - /** - * @namespace - * @memberof AttrRecognizer - */ - defaults: { - /** - * @type {Number} - * @default 1 - */ - pointers: 1 - }, - - /** - * Used to check if it the recognizer receives valid input, like input.distance > 10. - * @memberof AttrRecognizer - * @param {Object} input - * @returns {Boolean} recognized - */ - attrTest: function(input) { - var optionPointers = this.options.pointers; - return optionPointers === 0 || input.pointers.length === optionPointers; - }, - - /** - * Process the input and return the state for the recognizer - * @memberof AttrRecognizer - * @param {Object} input - * @returns {*} State - */ - process: function(input) { - var state = this.state; - var eventType = input.eventType; - - var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); - var isValid = this.attrTest(input); - - // on cancel input and we've recognized before, return STATE_CANCELLED - if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { - return state | STATE_CANCELLED; - } else if (isRecognized || isValid) { - if (eventType & INPUT_END) { - return state | STATE_ENDED; - } else if (!(state & STATE_BEGAN)) { - return STATE_BEGAN; - } - return state | STATE_CHANGED; - } - return STATE_FAILED; - } - }); - - /** - * Pan - * Recognized when the pointer is down and moved in the allowed direction. - * @constructor - * @extends AttrRecognizer - */ - function PanRecognizer() { - AttrRecognizer.apply(this, arguments); - - this.pX = null; - this.pY = null; - } - - inherit(PanRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof PanRecognizer - */ - defaults: { - event: 'pan', - threshold: 10, - pointers: 1, - direction: DIRECTION_ALL - }, - - getTouchAction: function() { - var direction = this.options.direction; - var actions = []; - if (direction & DIRECTION_HORIZONTAL) { - actions.push(TOUCH_ACTION_PAN_Y); - } - if (direction & DIRECTION_VERTICAL) { - actions.push(TOUCH_ACTION_PAN_X); - } - return actions; - }, - - directionTest: function(input) { - var options = this.options; - var hasMoved = true; - var distance = input.distance; - var direction = input.direction; - var x = input.deltaX; - var y = input.deltaY; - - // lock to axis? - if (!(direction & options.direction)) { - if (options.direction & DIRECTION_HORIZONTAL) { - direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; - hasMoved = x != this.pX; - distance = Math.abs(input.deltaX); - } else { - direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; - hasMoved = y != this.pY; - distance = Math.abs(input.deltaY); - } - } - input.direction = direction; - return hasMoved && distance > options.threshold && direction & options.direction; - }, - - attrTest: function(input) { - return AttrRecognizer.prototype.attrTest.call(this, input) && - (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); - }, - - emit: function(input) { - this.pX = input.deltaX; - this.pY = input.deltaY; - - var direction = directionStr(input.direction); - if (direction) { - this.manager.emit(this.options.event + direction, input); - } - - this._super.emit.call(this, input); - } - }); - - /** - * Pinch - * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). - * @constructor - * @extends AttrRecognizer - */ - function PinchRecognizer() { - AttrRecognizer.apply(this, arguments); - } - - inherit(PinchRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof PinchRecognizer - */ - defaults: { - event: 'pinch', - threshold: 0, - pointers: 2 - }, - - getTouchAction: function() { - return [TOUCH_ACTION_NONE]; - }, - - attrTest: function(input) { - return this._super.attrTest.call(this, input) && - (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); - }, - - emit: function(input) { - this._super.emit.call(this, input); - if (input.scale !== 1) { - var inOut = input.scale < 1 ? 'in' : 'out'; - this.manager.emit(this.options.event + inOut, input); - } - } - }); - - /** - * Press - * Recognized when the pointer is down for x ms without any movement. - * @constructor - * @extends Recognizer - */ - function PressRecognizer() { - Recognizer.apply(this, arguments); - - this._timer = null; - this._input = null; - } - - inherit(PressRecognizer, Recognizer, { - /** - * @namespace - * @memberof PressRecognizer - */ - defaults: { - event: 'press', - pointers: 1, - time: 500, // minimal time of the pointer to be pressed - threshold: 5 // a minimal movement is ok, but keep it low - }, - - getTouchAction: function() { - return [TOUCH_ACTION_AUTO]; - }, - - process: function(input) { - var options = this.options; - var validPointers = input.pointers.length === options.pointers; - var validMovement = input.distance < options.threshold; - var validTime = input.deltaTime > options.time; - - this._input = input; - - // we only allow little movement - // and we've reached an end event, so a tap is possible - if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { - this.reset(); - } else if (input.eventType & INPUT_START) { - this.reset(); - this._timer = setTimeoutContext(function() { - this.state = STATE_RECOGNIZED; - this.tryEmit(); - }, options.time, this); - } else if (input.eventType & INPUT_END) { - return STATE_RECOGNIZED; - } - return STATE_FAILED; - }, - - reset: function() { - clearTimeout(this._timer); - }, - - emit: function(input) { - if (this.state !== STATE_RECOGNIZED) { - return; - } - - if (input && (input.eventType & INPUT_END)) { - this.manager.emit(this.options.event + 'up', input); - } else { - this._input.timeStamp = now(); - this.manager.emit(this.options.event, this._input); - } - } - }); - - /** - * Rotate - * Recognized when two or more pointer are moving in a circular motion. - * @constructor - * @extends AttrRecognizer - */ - function RotateRecognizer() { - AttrRecognizer.apply(this, arguments); - } - - inherit(RotateRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof RotateRecognizer - */ - defaults: { - event: 'rotate', - threshold: 0, - pointers: 2 - }, - - getTouchAction: function() { - return [TOUCH_ACTION_NONE]; - }, - - attrTest: function(input) { - return this._super.attrTest.call(this, input) && - (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); - } - }); - - /** - * Swipe - * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. - * @constructor - * @extends AttrRecognizer - */ - function SwipeRecognizer() { - AttrRecognizer.apply(this, arguments); - } - - inherit(SwipeRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof SwipeRecognizer - */ - defaults: { - event: 'swipe', - threshold: 10, - velocity: 0.65, - direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, - pointers: 1 - }, - - getTouchAction: function() { - return PanRecognizer.prototype.getTouchAction.call(this); - }, - - attrTest: function(input) { - var direction = this.options.direction; - var velocity; - - if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { - velocity = input.velocity; - } else if (direction & DIRECTION_HORIZONTAL) { - velocity = input.velocityX; - } else if (direction & DIRECTION_VERTICAL) { - velocity = input.velocityY; - } - - return this._super.attrTest.call(this, input) && - direction & input.direction && - input.distance > this.options.threshold && - abs(velocity) > this.options.velocity && input.eventType & INPUT_END; - }, - - emit: function(input) { - var direction = directionStr(input.direction); - if (direction) { - this.manager.emit(this.options.event + direction, input); - } - - this.manager.emit(this.options.event, input); - } - }); - - /** - * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur - * between the given interval and position. The delay option can be used to recognize multi-taps without firing - * a single tap. - * - * The eventData from the emitted event contains the property `tapCount`, which contains the amount of - * multi-taps being recognized. - * @constructor - * @extends Recognizer - */ - function TapRecognizer() { - Recognizer.apply(this, arguments); - - // previous time and center, - // used for tap counting - this.pTime = false; - this.pCenter = false; - - this._timer = null; - this._input = null; - this.count = 0; - } - - inherit(TapRecognizer, Recognizer, { - /** - * @namespace - * @memberof PinchRecognizer - */ - defaults: { - event: 'tap', - pointers: 1, - taps: 1, - interval: 300, // max time between the multi-tap taps - time: 250, // max time of the pointer to be down (like finger on the screen) - threshold: 2, // a minimal movement is ok, but keep it low - posThreshold: 10 // a multi-tap can be a bit off the initial position - }, - - getTouchAction: function() { - return [TOUCH_ACTION_MANIPULATION]; - }, - - process: function(input) { - var options = this.options; - - var validPointers = input.pointers.length === options.pointers; - var validMovement = input.distance < options.threshold; - var validTouchTime = input.deltaTime < options.time; - - this.reset(); - - if ((input.eventType & INPUT_START) && (this.count === 0)) { - return this.failTimeout(); - } - - // we only allow little movement - // and we've reached an end event, so a tap is possible - if (validMovement && validTouchTime && validPointers) { - if (input.eventType != INPUT_END) { - return this.failTimeout(); - } - - var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; - var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; - - this.pTime = input.timeStamp; - this.pCenter = input.center; - - if (!validMultiTap || !validInterval) { - this.count = 1; - } else { - this.count += 1; - } - - this._input = input; - - // if tap count matches we have recognized it, - // else it has began recognizing... - var tapCount = this.count % options.taps; - if (tapCount === 0) { - // no failing requirements, immediately trigger the tap event - // or wait as long as the multitap interval to trigger - if (!this.hasRequireFailures()) { - return STATE_RECOGNIZED; - } else { - this._timer = setTimeoutContext(function() { - this.state = STATE_RECOGNIZED; - this.tryEmit(); - }, options.interval, this); - return STATE_BEGAN; - } - } - } - return STATE_FAILED; - }, - - failTimeout: function() { - this._timer = setTimeoutContext(function() { - this.state = STATE_FAILED; - }, this.options.interval, this); - return STATE_FAILED; - }, - - reset: function() { - clearTimeout(this._timer); - }, - - emit: function() { - if (this.state == STATE_RECOGNIZED ) { - this._input.tapCount = this.count; - this.manager.emit(this.options.event, this._input); - } - } - }); - - /** - * Simple way to create an manager with a default set of recognizers. - * @param {HTMLElement} element - * @param {Object} [options] - * @constructor - */ - function Hammer(element, options) { - options = options || {}; - options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); - return new Manager(element, options); - } - - /** - * @const {string} - */ - Hammer.VERSION = '2.0.4'; - - /** - * default settings - * @namespace - */ - Hammer.defaults = { - /** - * set if DOM events are being triggered. - * But this is slower and unused by simple implementations, so disabled by default. - * @type {Boolean} - * @default false - */ - domEvents: false, - - /** - * The value for the touchAction property/fallback. - * When set to `compute` it will magically set the correct value based on the added recognizers. - * @type {String} - * @default compute - */ - touchAction: TOUCH_ACTION_COMPUTE, - - /** - * @type {Boolean} - * @default true - */ - enable: true, - - /** - * EXPERIMENTAL FEATURE -- can be removed/changed - * Change the parent input target element. - * If Null, then it is being set the to main element. - * @type {Null|EventTarget} - * @default null - */ - inputTarget: null, - - /** - * force an input class - * @type {Null|Function} - * @default null - */ - inputClass: null, - - /** - * Default recognizer setup when calling `Hammer()` - * When creating a new Manager these will be skipped. - * @type {Array} - */ - preset: [ - // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] - [RotateRecognizer, { enable: false }], - [PinchRecognizer, { enable: false }, ['rotate']], - [SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }], - [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], - [TapRecognizer], - [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], - [PressRecognizer] - ], - - /** - * Some CSS properties can be used to improve the working of Hammer. - * Add them to this method and they will be set when creating a new Manager. - * @namespace - */ - cssProps: { - /** - * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. - * @type {String} - * @default 'none' - */ - userSelect: 'none', - - /** - * Disable the Windows Phone grippers when pressing an element. - * @type {String} - * @default 'none' - */ - touchSelect: 'none', - - /** - * Disables the default callout shown when you touch and hold a touch target. - * On iOS, when you touch and hold a touch target such as a link, Safari displays - * a callout containing information about the link. This property allows you to disable that callout. - * @type {String} - * @default 'none' - */ - touchCallout: 'none', - - /** - * Specifies whether zooming is enabled. Used by IE10> - * @type {String} - * @default 'none' - */ - contentZooming: 'none', - - /** - * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. - * @type {String} - * @default 'none' - */ - userDrag: 'none', - - /** - * Overrides the highlight color shown when the user taps a link or a JavaScript - * clickable element in iOS. This property obeys the alpha value, if specified. - * @type {String} - * @default 'rgba(0,0,0,0)' - */ - tapHighlightColor: 'rgba(0,0,0,0)' - } - }; - - var STOP = 1; - var FORCED_STOP = 2; - - /** - * Manager - * @param {HTMLElement} element - * @param {Object} [options] - * @constructor - */ - function Manager(element, options) { - options = options || {}; - - this.options = merge(options, Hammer.defaults); - this.options.inputTarget = this.options.inputTarget || element; - - this.handlers = {}; - this.session = {}; - this.recognizers = []; - - this.element = element; - this.input = createInputInstance(this); - this.touchAction = new TouchAction(this, this.options.touchAction); - - toggleCssProps(this, true); - - each(options.recognizers, function(item) { - var recognizer = this.add(new (item[0])(item[1])); - item[2] && recognizer.recognizeWith(item[2]); - item[3] && recognizer.requireFailure(item[3]); - }, this); - } - - Manager.prototype = { - /** - * set options - * @param {Object} options - * @returns {Manager} - */ - set: function(options) { - extend(this.options, options); - - // Options that need a little more setup - if (options.touchAction) { - this.touchAction.update(); - } - if (options.inputTarget) { - // Clean up existing event listeners and reinitialize - this.input.destroy(); - this.input.target = options.inputTarget; - this.input.init(); - } - return this; - }, - - /** - * stop recognizing for this session. - * This session will be discarded, when a new [input]start event is fired. - * When forced, the recognizer cycle is stopped immediately. - * @param {Boolean} [force] - */ - stop: function(force) { - this.session.stopped = force ? FORCED_STOP : STOP; - }, - - /** - * run the recognizers! - * called by the inputHandler function on every movement of the pointers (touches) - * it walks through all the recognizers and tries to detect the gesture that is being made - * @param {Object} inputData - */ - recognize: function(inputData) { - var session = this.session; - if (session.stopped) { - return; - } - - // run the touch-action polyfill - this.touchAction.preventDefaults(inputData); - - var recognizer; - var recognizers = this.recognizers; - - // this holds the recognizer that is being recognized. - // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED - // if no recognizer is detecting a thing, it is set to `null` - var curRecognizer = session.curRecognizer; - - // reset when the last recognizer is recognized - // or when we're in a new session - if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { - curRecognizer = session.curRecognizer = null; - } - - var i = 0; - while (i < recognizers.length) { - recognizer = recognizers[i]; - - // find out if we are allowed try to recognize the input for this one. - // 1. allow if the session is NOT forced stopped (see the .stop() method) - // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one - // that is being recognized. - // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. - // this can be setup with the `recognizeWith()` method on the recognizer. - if (session.stopped !== FORCED_STOP && ( // 1 - !curRecognizer || recognizer == curRecognizer || // 2 - recognizer.canRecognizeWith(curRecognizer))) { // 3 - recognizer.recognize(inputData); - } else { - recognizer.reset(); - } - - // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the - // current active recognizer. but only if we don't already have an active recognizer - if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { - curRecognizer = session.curRecognizer = recognizer; - } - i++; - } - }, - - /** - * get a recognizer by its event name. - * @param {Recognizer|String} recognizer - * @returns {Recognizer|Null} - */ - get: function(recognizer) { - if (recognizer instanceof Recognizer) { - return recognizer; - } - - var recognizers = this.recognizers; - for (var i = 0; i < recognizers.length; i++) { - if (recognizers[i].options.event == recognizer) { - return recognizers[i]; - } - } - return null; - }, - - /** - * add a recognizer to the manager - * existing recognizers with the same event name will be removed - * @param {Recognizer} recognizer - * @returns {Recognizer|Manager} - */ - add: function(recognizer) { - if (invokeArrayArg(recognizer, 'add', this)) { - return this; - } - - // remove existing - var existing = this.get(recognizer.options.event); - if (existing) { - this.remove(existing); - } - - this.recognizers.push(recognizer); - recognizer.manager = this; - - this.touchAction.update(); - return recognizer; - }, - - /** - * remove a recognizer by name or instance - * @param {Recognizer|String} recognizer - * @returns {Manager} - */ - remove: function(recognizer) { - if (invokeArrayArg(recognizer, 'remove', this)) { - return this; - } - - var recognizers = this.recognizers; - recognizer = this.get(recognizer); - recognizers.splice(inArray(recognizers, recognizer), 1); - - this.touchAction.update(); - return this; - }, - - /** - * bind event - * @param {String} events - * @param {Function} handler - * @returns {EventEmitter} this - */ - on: function(events, handler) { - var handlers = this.handlers; - each(splitStr(events), function(event) { - handlers[event] = handlers[event] || []; - handlers[event].push(handler); - }); - return this; - }, - - /** - * unbind event, leave emit blank to remove all handlers - * @param {String} events - * @param {Function} [handler] - * @returns {EventEmitter} this - */ - off: function(events, handler) { - var handlers = this.handlers; - each(splitStr(events), function(event) { - if (!handler) { - delete handlers[event]; - } else { - handlers[event].splice(inArray(handlers[event], handler), 1); - } - }); - return this; - }, - - /** - * emit event to the listeners - * @param {String} event - * @param {Object} data - */ - emit: function(event, data) { - // we also want to trigger dom events - if (this.options.domEvents) { - triggerDomEvent(event, data); - } - - // no handlers, so skip it all - var handlers = this.handlers[event] && this.handlers[event].slice(); - if (!handlers || !handlers.length) { - return; - } - - data.type = event; - data.preventDefault = function() { - data.srcEvent.preventDefault(); - }; - - var i = 0; - while (i < handlers.length) { - handlers[i](data); - i++; - } - }, - - /** - * destroy the manager and unbinds all events - * it doesn't unbind dom events, that is the user own responsibility - */ - destroy: function() { - this.element && toggleCssProps(this, false); - - this.handlers = {}; - this.session = {}; - this.input.destroy(); - this.element = null; - } - }; - - /** - * add/remove the css properties as defined in manager.options.cssProps - * @param {Manager} manager - * @param {Boolean} add - */ - function toggleCssProps(manager, add) { - var element = manager.element; - each(manager.options.cssProps, function(value, name) { - element.style[prefixed(element.style, name)] = add ? value : ''; - }); - } - - /** - * trigger dom event - * @param {String} event - * @param {Object} data - */ - function triggerDomEvent(event, data) { - var gestureEvent = document.createEvent('Event'); - gestureEvent.initEvent(event, true, true); - gestureEvent.gesture = data; - data.target.dispatchEvent(gestureEvent); - } - - extend(Hammer, { - INPUT_START: INPUT_START, - INPUT_MOVE: INPUT_MOVE, - INPUT_END: INPUT_END, - INPUT_CANCEL: INPUT_CANCEL, - - STATE_POSSIBLE: STATE_POSSIBLE, - STATE_BEGAN: STATE_BEGAN, - STATE_CHANGED: STATE_CHANGED, - STATE_ENDED: STATE_ENDED, - STATE_RECOGNIZED: STATE_RECOGNIZED, - STATE_CANCELLED: STATE_CANCELLED, - STATE_FAILED: STATE_FAILED, - - DIRECTION_NONE: DIRECTION_NONE, - DIRECTION_LEFT: DIRECTION_LEFT, - DIRECTION_RIGHT: DIRECTION_RIGHT, - DIRECTION_UP: DIRECTION_UP, - DIRECTION_DOWN: DIRECTION_DOWN, - DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, - DIRECTION_VERTICAL: DIRECTION_VERTICAL, - DIRECTION_ALL: DIRECTION_ALL, - - Manager: Manager, - Input: Input, - TouchAction: TouchAction, - - TouchInput: TouchInput, - MouseInput: MouseInput, - PointerEventInput: PointerEventInput, - TouchMouseInput: TouchMouseInput, - SingleTouchInput: SingleTouchInput, - - Recognizer: Recognizer, - AttrRecognizer: AttrRecognizer, - Tap: TapRecognizer, - Pan: PanRecognizer, - Swipe: SwipeRecognizer, - Pinch: PinchRecognizer, - Rotate: RotateRecognizer, - Press: PressRecognizer, - - on: addEventListeners, - off: removeEventListeners, - each: each, - merge: merge, - extend: extend, - inherit: inherit, - bindFn: bindFn, - prefixed: prefixed - }); - - if ("function" == TYPE_FUNCTION && __webpack_require__(23)) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { - return Hammer; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof module != 'undefined' && module.exports) { - module.exports = Hammer; - } else { - window[exportName] = Hammer; - } - - })(window, document, 'Hammer'); + var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.4 - 2014-09-28 + * http://hammerjs.github.io/ + * + * Copyright (c) 2014 Jorik Tangelder; + * Licensed under the MIT license */ + (function(window, document, exportName, undefined) { + 'use strict'; + var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o']; + var TEST_ELEMENT = document.createElement('div'); -/***/ }, -/* 23 */ -/***/ function(module, exports) { + var TYPE_FUNCTION = 'function'; - /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; + var round = Math.round; + var abs = Math.abs; + var now = Date.now; - /* WEBPACK VAR INJECTION */}.call(exports, {})) + /** + * set a timeout with a given scope + * @param {Function} fn + * @param {Number} timeout + * @param {Object} context + * @returns {number} + */ + function setTimeoutContext(fn, timeout, context) { + return setTimeout(bindFn(fn, context), timeout); + } -/***/ }, -/* 24 */ -/***/ function(module, exports, __webpack_require__) { + /** + * if the argument is an array, we want to execute the fn on each entry + * if it aint an array we don't want to do a thing. + * this is used by all the methods that accept a single and array argument. + * @param {*|Array} arg + * @param {String} fn + * @param {Object} [context] + * @returns {Boolean} + */ + function invokeArrayArg(arg, fn, context) { + if (Array.isArray(arg)) { + each(arg, context[fn], context); + return true; + } + return false; + } - 'use strict'; + /** + * walk objects and arrays + * @param {Object} obj + * @param {Function} iterator + * @param {Object} context + */ + function each(obj, iterator, context) { + var i; - var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(25); - var moment = __webpack_require__(2); - var Component = __webpack_require__(26); - var DateUtil = __webpack_require__(27); + if (!obj) { + return; + } + + if (obj.forEach) { + obj.forEach(iterator, context); + } else if (obj.length !== undefined) { + i = 0; + while (i < obj.length) { + iterator.call(context, obj[i], i, obj); + i++; + } + } else { + for (i in obj) { + obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); + } + } + } /** - * @constructor Range - * A Range controls a numeric range with a start and end value. - * The Range adjusts the range based on mouse events or programmatic changes, - * and triggers events when the range is changing or has been changed. - * @param {{dom: Object, domProps: Object, emitter: Emitter}} body - * @param {Object} [options] See description at Range.setOptions + * extend object. + * means that properties in dest will be overwritten by the ones in src. + * @param {Object} dest + * @param {Object} src + * @param {Boolean} [merge] + * @returns {Object} dest */ - function Range(body, options) { - var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); - this.start = now.clone().add(-3, 'days').valueOf(); // Number - this.end = now.clone().add(4, 'days').valueOf(); // Number + function extend(dest, src, merge) { + var keys = Object.keys(src); + var i = 0; + while (i < keys.length) { + if (!merge || (merge && dest[keys[i]] === undefined)) { + dest[keys[i]] = src[keys[i]]; + } + i++; + } + return dest; + } - this.body = body; - this.deltaDifference = 0; - this.scaleOffset = 0; - this.startToFront = false; - this.endToFront = true; + /** + * merge the values from src in the dest. + * means that properties that exist in dest will not be overwritten by src + * @param {Object} dest + * @param {Object} src + * @returns {Object} dest + */ + function merge(dest, src) { + return extend(dest, src, true); + } - // default options - this.defaultOptions = { - start: null, - end: null, - moment: moment, - direction: 'horizontal', // 'horizontal' or 'vertical' - moveable: true, - zoomable: true, - min: null, - max: null, - zoomMin: 10, // milliseconds - zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds - }; - this.options = util.extend({}, this.defaultOptions); + /** + * simple class inheritance + * @param {Function} child + * @param {Function} base + * @param {Object} [properties] + */ + function inherit(child, base, properties) { + var baseP = base.prototype, + childP; - this.props = { - touch: {} - }; - this.animationTimer = null; + childP = child.prototype = Object.create(baseP); + childP.constructor = child; + childP._super = baseP; - // drag listeners for dragging - this.body.emitter.on('panstart', this._onDragStart.bind(this)); - this.body.emitter.on('panmove', this._onDrag.bind(this)); - this.body.emitter.on('panend', this._onDragEnd.bind(this)); + if (properties) { + extend(childP, properties); + } + } - // mouse wheel for zooming - this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); + /** + * simple function bind + * @param {Function} fn + * @param {Object} context + * @returns {Function} + */ + function bindFn(fn, context) { + return function boundFn() { + return fn.apply(context, arguments); + }; + } - // pinch to zoom - this.body.emitter.on('touch', this._onTouch.bind(this)); - this.body.emitter.on('pinch', this._onPinch.bind(this)); + /** + * let a boolean value also be a function that must return a boolean + * this first item in args will be used as the context + * @param {Boolean|Function} val + * @param {Array} [args] + * @returns {Boolean} + */ + function boolOrFn(val, args) { + if (typeof val == TYPE_FUNCTION) { + return val.apply(args ? args[0] || undefined : undefined, args); + } + return val; + } - this.setOptions(options); + /** + * use the val2 when val1 is undefined + * @param {*} val1 + * @param {*} val2 + * @returns {*} + */ + function ifUndefined(val1, val2) { + return (val1 === undefined) ? val2 : val1; } - Range.prototype = new Component(); + /** + * addEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler + */ + function addEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.addEventListener(type, handler, false); + }); + } /** - * Set options for the range controller - * @param {Object} options Available options: - * {Number | Date | String} start Start date for the range - * {Number | Date | String} end End date for the range - * {Number} min Minimum value for start - * {Number} max Maximum value for end - * {Number} zoomMin Set a minimum value for - * (end - start). - * {Number} zoomMax Set a maximum value for - * (end - start). - * {Boolean} moveable Enable moving of the range - * by dragging. True by default - * {Boolean} zoomable Enable zooming of the range - * by pinching/scrolling. True by default + * removeEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler */ - Range.prototype.setOptions = function (options) { - if (options) { - // copy the options that we know - var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey']; - util.selectiveExtend(fields, this.options, options); + function removeEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.removeEventListener(type, handler, false); + }); + } - if ('start' in options || 'end' in options) { - // apply a new range. both start and end are optional - this.setRange(options.start, options.end); + /** + * find if a node is in the given parent + * @method hasParent + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @return {Boolean} found + */ + function hasParent(node, parent) { + while (node) { + if (node == parent) { + return true; + } + node = node.parentNode; } - } - }; + return false; + } /** - * Test whether direction has a valid value - * @param {String} direction 'horizontal' or 'vertical' + * small indexOf wrapper + * @param {String} str + * @param {String} find + * @returns {Boolean} found */ - function validateDirection(direction) { - if (direction != 'horizontal' && direction != 'vertical') { - throw new TypeError('Unknown direction "' + direction + '". ' + 'Choose "horizontal" or "vertical".'); - } + function inStr(str, find) { + return str.indexOf(find) > -1; } /** - * Set a new start and end range - * @param {Date | Number | String} [start] - * @param {Date | Number | String} [end] - * @param {boolean | {duration: number, easingFunction: string}} [animation=false] - * If true (default), the range is animated - * smoothly to the new window. An object can be - * provided to specify duration and easing function. - * Default duration is 500 ms, and default easing - * function is 'easeInOutQuad'. - * @param {Boolean} [byUser=false] - * + * split string on whitespace + * @param {String} str + * @returns {Array} words */ - Range.prototype.setRange = function (start, end, animation, byUser) { - if (byUser !== true) { - byUser = false; - } - var finalStart = start != undefined ? util.convert(start, 'Date').valueOf() : null; - var finalEnd = end != undefined ? util.convert(end, 'Date').valueOf() : null; - this._cancelAnimation(); + function splitStr(str) { + return str.trim().split(/\s+/g); + } - if (animation) { - // true or an Object - var me = this; - var initStart = this.start; - var initEnd = this.end; - var duration = typeof animation === 'object' && 'duration' in animation ? animation.duration : 500; - var easingName = typeof animation === 'object' && 'easingFunction' in animation ? animation.easingFunction : 'easeInOutQuad'; - var easingFunction = util.easingFunctions[easingName]; - if (!easingFunction) { - throw new Error('Unknown easing function ' + JSON.stringify(easingName) + '. ' + 'Choose from: ' + Object.keys(util.easingFunctions).join(', ')); + /** + * find if a array contains the object using indexOf or a simple polyFill + * @param {Array} src + * @param {String} find + * @param {String} [findByKey] + * @return {Boolean|Number} false when not found, or the index + */ + function inArray(src, find, findByKey) { + if (src.indexOf && !findByKey) { + return src.indexOf(find); + } else { + var i = 0; + while (i < src.length) { + if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { + return i; + } + i++; + } + return -1; } + } - var initTime = new Date().valueOf(); - var anyChanged = false; + /** + * convert array-like objects to real arrays + * @param {Object} obj + * @returns {Array} + */ + function toArray(obj) { + return Array.prototype.slice.call(obj, 0); + } - var next = function next() { - if (!me.props.touch.dragging) { - var now = new Date().valueOf(); - var time = now - initTime; - var ease = easingFunction(time / duration); - var done = time > duration; - var s = done || finalStart === null ? finalStart : initStart + (finalStart - initStart) * ease; - var e = done || finalEnd === null ? finalEnd : initEnd + (finalEnd - initEnd) * ease; + /** + * unique array with objects based on a key (like 'id') or just by the array's value + * @param {Array} src [{id:1},{id:2},{id:1}] + * @param {String} [key] + * @param {Boolean} [sort=False] + * @returns {Array} [{id:1},{id:2}] + */ + function uniqueArray(src, key, sort) { + var results = []; + var values = []; + var i = 0; - changed = me._applyRange(s, e); - DateUtil.updateHiddenDates(me.options.moment, me.body, me.options.hiddenDates); - anyChanged = anyChanged || changed; - if (changed) { - me.body.emitter.emit('rangechange', { start: new Date(me.start), end: new Date(me.end), byUser: byUser }); + while (i < src.length) { + var val = key ? src[i][key] : src[i]; + if (inArray(values, val) < 0) { + results.push(src[i]); } + values[i] = val; + i++; + } - if (done) { - if (anyChanged) { - me.body.emitter.emit('rangechanged', { start: new Date(me.start), end: new Date(me.end), byUser: byUser }); - } + if (sort) { + if (!key) { + results = results.sort(); } else { - // animate with as high as possible frame rate, leave 20 ms in between - // each to prevent the browser from blocking - me.animationTimer = setTimeout(next, 20); + results = results.sort(function sortUniqueArray(a, b) { + return a[key] > b[key]; + }); } - } - }; + } - return next(); - } else { - var changed = this._applyRange(finalStart, finalEnd); - DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates); - if (changed) { - var params = { start: new Date(this.start), end: new Date(this.end), byUser: byUser }; - this.body.emitter.emit('rangechange', params); - this.body.emitter.emit('rangechanged', params); + return results; + } + + /** + * get the prefixed property + * @param {Object} obj + * @param {String} property + * @returns {String|Undefined} prefixed + */ + function prefixed(obj, property) { + var prefix, prop; + var camelProp = property[0].toUpperCase() + property.slice(1); + + var i = 0; + while (i < VENDOR_PREFIXES.length) { + prefix = VENDOR_PREFIXES[i]; + prop = (prefix) ? prefix + camelProp : property; + + if (prop in obj) { + return prop; + } + i++; } - } - }; + return undefined; + } /** - * Stop an animation - * @private + * get a unique id + * @returns {number} uniqueId */ - Range.prototype._cancelAnimation = function () { - if (this.animationTimer) { - clearTimeout(this.animationTimer); - this.animationTimer = null; - } - }; + var _uniqueId = 1; + function uniqueId() { + return _uniqueId++; + } /** - * Set a new start and end range. This method is the same as setRange, but - * does not trigger a range change and range changed event, and it returns - * true when the range is changed - * @param {Number} [start] - * @param {Number} [end] - * @return {Boolean} changed - * @private + * get the window object of an element + * @param {HTMLElement} element + * @returns {DocumentView|Window} */ - Range.prototype._applyRange = function (start, end) { - var newStart = start != null ? util.convert(start, 'Date').valueOf() : this.start, - newEnd = end != null ? util.convert(end, 'Date').valueOf() : this.end, - max = this.options.max != null ? util.convert(this.options.max, 'Date').valueOf() : null, - min = this.options.min != null ? util.convert(this.options.min, 'Date').valueOf() : null, - diff; + function getWindowForElement(element) { + var doc = element.ownerDocument; + return (doc.defaultView || doc.parentWindow); + } - // check for valid number - if (isNaN(newStart) || newStart === null) { - throw new Error('Invalid start "' + start + '"'); - } - if (isNaN(newEnd) || newEnd === null) { - throw new Error('Invalid end "' + end + '"'); - } + var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; - // prevent start < end - if (newEnd < newStart) { - newEnd = newStart; - } + var SUPPORT_TOUCH = ('ontouchstart' in window); + var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; + var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); - // prevent start < min - if (min !== null) { - if (newStart < min) { - diff = min - newStart; - newStart += diff; - newEnd += diff; + var INPUT_TYPE_TOUCH = 'touch'; + var INPUT_TYPE_PEN = 'pen'; + var INPUT_TYPE_MOUSE = 'mouse'; + var INPUT_TYPE_KINECT = 'kinect'; - // prevent end > max - if (max != null) { - if (newEnd > max) { - newEnd = max; - } - } - } - } + var COMPUTE_INTERVAL = 25; - // prevent end > max - if (max !== null) { - if (newEnd > max) { - diff = newEnd - max; - newStart -= diff; - newEnd -= diff; + var INPUT_START = 1; + var INPUT_MOVE = 2; + var INPUT_END = 4; + var INPUT_CANCEL = 8; - // prevent start < min - if (min != null) { - if (newStart < min) { - newStart = min; - } - } - } - } + var DIRECTION_NONE = 1; + var DIRECTION_LEFT = 2; + var DIRECTION_RIGHT = 4; + var DIRECTION_UP = 8; + var DIRECTION_DOWN = 16; - // prevent (end-start) < zoomMin - if (this.options.zoomMin !== null) { - var zoomMin = parseFloat(this.options.zoomMin); - if (zoomMin < 0) { - zoomMin = 0; - } - if (newEnd - newStart < zoomMin) { - if (this.end - this.start === zoomMin && newStart > this.start && newEnd < this.end) { - // ignore this action, we are already zoomed to the minimum - newStart = this.start; - newEnd = this.end; - } else { - // zoom to the minimum - diff = zoomMin - (newEnd - newStart); - newStart -= diff / 2; - newEnd += diff / 2; - } - } - } + var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; + var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; + var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; - // prevent (end-start) > zoomMax - if (this.options.zoomMax !== null) { - var zoomMax = parseFloat(this.options.zoomMax); - if (zoomMax < 0) { - zoomMax = 0; - } + var PROPS_XY = ['x', 'y']; + var PROPS_CLIENT_XY = ['clientX', 'clientY']; - if (newEnd - newStart > zoomMax) { - if (this.end - this.start === zoomMax && newStart < this.start && newEnd > this.end) { - // ignore this action, we are already zoomed to the maximum - newStart = this.start; - newEnd = this.end; - } else { - // zoom to the maximum - diff = newEnd - newStart - zoomMax; - newStart += diff / 2; - newEnd -= diff / 2; - } - } - } + /** + * create new input type manager + * @param {Manager} manager + * @param {Function} callback + * @returns {Input} + * @constructor + */ + function Input(manager, callback) { + var self = this; + this.manager = manager; + this.callback = callback; + this.element = manager.element; + this.target = manager.options.inputTarget; - var changed = this.start != newStart || this.end != newEnd; + // smaller wrapper around the handler, for the scope and the enabled state of the manager, + // so when disabled the input events are completely bypassed. + this.domHandler = function(ev) { + if (boolOrFn(manager.options.enable, [manager])) { + self.handler(ev); + } + }; - // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not necessarily of type Range) - if (!(newStart >= this.start && newStart <= this.end || newEnd >= this.start && newEnd <= this.end) && !(this.start >= newStart && this.start <= newEnd || this.end >= newStart && this.end <= newEnd)) { - this.body.emitter.emit('checkRangedItems'); - } + this.init(); - this.start = newStart; - this.end = newEnd; - return changed; - }; + } - /** - * Retrieve the current range. - * @return {Object} An object with start and end properties - */ - Range.prototype.getRange = function () { - return { - start: this.start, - end: this.end - }; - }; + Input.prototype = { + /** + * should handle the inputEvent data and trigger the callback + * @virtual + */ + handler: function() { }, - /** - * Calculate the conversion offset and scale for current range, based on - * the provided width - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion - */ - Range.prototype.conversion = function (width, totalHidden) { - return Range.conversion(this.start, this.end, width, totalHidden); + /** + * bind the events + */ + init: function() { + this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + }, + + /** + * unbind the events + */ + destroy: function() { + this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + } }; /** - * Static method to calculate the conversion offset and scale for a range, - * based on the provided start, end, and width - * @param {Number} start - * @param {Number} end - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion + * create new input type manager + * called by the Manager constructor + * @param {Hammer} manager + * @returns {Input} */ - Range.conversion = function (start, end, width, totalHidden) { - if (totalHidden === undefined) { - totalHidden = 0; - } - if (width != 0 && end - start != 0) { - return { - offset: start, - scale: width / (end - start - totalHidden) - }; - } else { - return { - offset: 0, - scale: 1 - }; - } - }; + function createInputInstance(manager) { + var Type; + var inputClass = manager.options.inputClass; + + if (inputClass) { + Type = inputClass; + } else if (SUPPORT_POINTER_EVENTS) { + Type = PointerEventInput; + } else if (SUPPORT_ONLY_TOUCH) { + Type = TouchInput; + } else if (!SUPPORT_TOUCH) { + Type = MouseInput; + } else { + Type = TouchMouseInput; + } + return new (Type)(manager, inputHandler); + } /** - * Start dragging horizontally or vertically - * @param {Event} event - * @private + * handle input events + * @param {Manager} manager + * @param {String} eventType + * @param {Object} input */ - Range.prototype._onDragStart = function (event) { - this.deltaDifference = 0; - this.previousDelta = 0; + function inputHandler(manager, eventType, input) { + var pointersLen = input.pointers.length; + var changedPointersLen = input.changedPointers.length; + var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); + var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); - // only allow dragging when configured as movable - if (!this.options.moveable) return; + input.isFirst = !!isFirst; + input.isFinal = !!isFinal; - // only start dragging when the mouse is inside the current range - if (!this._isInsideRange(event)) return; + if (isFirst) { + manager.session = {}; + } - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; + // source event is the normalized value of the domEvents + // like 'touchstart, mouseup, pointerdown' + input.eventType = eventType; - this.props.touch.start = this.start; - this.props.touch.end = this.end; - this.props.touch.dragging = true; + // compute scale, rotation etc + computeInputData(manager, input); - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'move'; - } - }; + // emit secret event + manager.emit('hammer.input', input); + + manager.recognize(input); + manager.session.prevInput = input; + } /** - * Perform dragging operation - * @param {Event} event - * @private + * extend the data with some usable properties like scale, rotate, velocity etc + * @param {Object} manager + * @param {Object} input */ - Range.prototype._onDrag = function (event) { - if (!this.props.touch.dragging) return; + function computeInputData(manager, input) { + var session = manager.session; + var pointers = input.pointers; + var pointersLength = pointers.length; - // only allow dragging when configured as movable - if (!this.options.moveable) return; + // store the first input to calculate the distance and direction + if (!session.firstInput) { + session.firstInput = simpleCloneInputData(input); + } - // TODO: this may be redundant in hammerjs2 - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; + // to compute scale and rotation we need to store the multiple touches + if (pointersLength > 1 && !session.firstMultiple) { + session.firstMultiple = simpleCloneInputData(input); + } else if (pointersLength === 1) { + session.firstMultiple = false; + } - var direction = this.options.direction; - validateDirection(direction); - var delta = direction == 'horizontal' ? event.deltaX : event.deltaY; - delta -= this.deltaDifference; - var interval = this.props.touch.end - this.props.touch.start; + var firstInput = session.firstInput; + var firstMultiple = session.firstMultiple; + var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; - // normalize dragging speed if cutout is in between. - var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - interval -= duration; + var center = input.center = getCenter(pointers); + input.timeStamp = now(); + input.deltaTime = input.timeStamp - firstInput.timeStamp; - var width = direction == 'horizontal' ? this.body.domProps.center.width : this.body.domProps.center.height; - var diffRange = -delta / width * interval; - var newStart = this.props.touch.start + diffRange; - var newEnd = this.props.touch.end + diffRange; + input.angle = getAngle(offsetCenter, center); + input.distance = getDistance(offsetCenter, center); - // snapping times away from hidden zones + computeDeltaXY(session, input); + input.offsetDirection = getDirection(input.deltaX, input.deltaY); + + input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; + input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; + + computeIntervalInputData(session, input); + + // find the correct target + var target = manager.element; + if (hasParent(input.srcEvent.target, target)) { + target = input.srcEvent.target; + } + input.target = target; + } + + function computeDeltaXY(session, input) { + var center = input.center; + var offset = session.offsetDelta || {}; + var prevDelta = session.prevDelta || {}; + var prevInput = session.prevInput || {}; + + if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { + prevDelta = session.prevDelta = { + x: prevInput.deltaX || 0, + y: prevInput.deltaY || 0 + }; + + offset = session.offsetDelta = { + x: center.x, + y: center.y + }; + } + + input.deltaX = prevDelta.x + (center.x - offset.x); + input.deltaY = prevDelta.y + (center.y - offset.y); + } + + /** + * velocity is calculated every x ms + * @param {Object} session + * @param {Object} input + */ + function computeIntervalInputData(session, input) { + var last = session.lastInterval || input, + deltaTime = input.timeStamp - last.timeStamp, + velocity, velocityX, velocityY, direction; + + if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { + var deltaX = last.deltaX - input.deltaX; + var deltaY = last.deltaY - input.deltaY; + + var v = getVelocity(deltaTime, deltaX, deltaY); + velocityX = v.x; + velocityY = v.y; + velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; + direction = getDirection(deltaX, deltaY); + + session.lastInterval = input; + } else { + // use latest velocity info if it doesn't overtake a minimum period + velocity = last.velocity; + velocityX = last.velocityX; + velocityY = last.velocityY; + direction = last.direction; + } + + input.velocity = velocity; + input.velocityX = velocityX; + input.velocityY = velocityY; + input.direction = direction; + } + + /** + * create a simple clone from the input used for storage of firstInput and firstMultiple + * @param {Object} input + * @returns {Object} clonedInputData + */ + function simpleCloneInputData(input) { + // make a simple copy of the pointers because we will get a reference if we don't + // we only need clientXY for the calculations + var pointers = []; + var i = 0; + while (i < input.pointers.length) { + pointers[i] = { + clientX: round(input.pointers[i].clientX), + clientY: round(input.pointers[i].clientY) + }; + i++; + } + + return { + timeStamp: now(), + pointers: pointers, + center: getCenter(pointers), + deltaX: input.deltaX, + deltaY: input.deltaY + }; + } + + /** + * get the center of all the pointers + * @param {Array} pointers + * @return {Object} center contains `x` and `y` properties + */ + function getCenter(pointers) { + var pointersLength = pointers.length; + + // no need to loop when only one touch + if (pointersLength === 1) { + return { + x: round(pointers[0].clientX), + y: round(pointers[0].clientY) + }; + } + + var x = 0, y = 0, i = 0; + while (i < pointersLength) { + x += pointers[i].clientX; + y += pointers[i].clientY; + i++; + } + + return { + x: round(x / pointersLength), + y: round(y / pointersLength) + }; + } + + /** + * calculate the velocity between two points. unit is in px per ms. + * @param {Number} deltaTime + * @param {Number} x + * @param {Number} y + * @return {Object} velocity `x` and `y` + */ + function getVelocity(deltaTime, x, y) { + return { + x: x / deltaTime || 0, + y: y / deltaTime || 0 + }; + } + + /** + * get the direction between two points + * @param {Number} x + * @param {Number} y + * @return {Number} direction + */ + function getDirection(x, y) { + if (x === y) { + return DIRECTION_NONE; + } + + if (abs(x) >= abs(y)) { + return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return y > 0 ? DIRECTION_UP : DIRECTION_DOWN; + } + + /** + * calculate the absolute distance between two points + * @param {Object} p1 {x, y} + * @param {Object} p2 {x, y} + * @param {Array} [props] containing x and y keys + * @return {Number} distance + */ + function getDistance(p1, p2, props) { + if (!props) { + props = PROPS_XY; + } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; + + return Math.sqrt((x * x) + (y * y)); + } + + /** + * calculate the angle between two coordinates + * @param {Object} p1 + * @param {Object} p2 + * @param {Array} [props] containing x and y keys + * @return {Number} angle + */ + function getAngle(p1, p2, props) { + if (!props) { + props = PROPS_XY; + } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; + return Math.atan2(y, x) * 180 / Math.PI; + } + + /** + * calculate the rotation degrees between two pointersets + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} rotation + */ + function getRotation(start, end) { + return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY); + } + + /** + * calculate the scale factor between two pointersets + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} scale + */ + function getScale(start, end) { + return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); + } + + var MOUSE_INPUT_MAP = { + mousedown: INPUT_START, + mousemove: INPUT_MOVE, + mouseup: INPUT_END + }; + + var MOUSE_ELEMENT_EVENTS = 'mousedown'; + var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; + + /** + * Mouse events input + * @constructor + * @extends Input + */ + function MouseInput() { + this.evEl = MOUSE_ELEMENT_EVENTS; + this.evWin = MOUSE_WINDOW_EVENTS; + + this.allow = true; // used by Input.TouchMouse to disable mouse events + this.pressed = false; // mousedown state + + Input.apply(this, arguments); + } + + inherit(MouseInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function MEhandler(ev) { + var eventType = MOUSE_INPUT_MAP[ev.type]; + + // on start we want to have the left mouse button down + if (eventType & INPUT_START && ev.button === 0) { + this.pressed = true; + } + + if (eventType & INPUT_MOVE && ev.which !== 1) { + eventType = INPUT_END; + } + + // mouse must be down, and mouse events are allowed (see the TouchMouse input) + if (!this.pressed || !this.allow) { + return; + } + + if (eventType & INPUT_END) { + this.pressed = false; + } + + this.callback(this.manager, eventType, { + pointers: [ev], + changedPointers: [ev], + pointerType: INPUT_TYPE_MOUSE, + srcEvent: ev + }); + } + }); + + var POINTER_INPUT_MAP = { + pointerdown: INPUT_START, + pointermove: INPUT_MOVE, + pointerup: INPUT_END, + pointercancel: INPUT_CANCEL, + pointerout: INPUT_CANCEL + }; + + // in IE10 the pointer types is defined as an enum + var IE10_POINTER_TYPE_ENUM = { + 2: INPUT_TYPE_TOUCH, + 3: INPUT_TYPE_PEN, + 4: INPUT_TYPE_MOUSE, + 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 + }; + + var POINTER_ELEMENT_EVENTS = 'pointerdown'; + var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; + + // IE10 has prefixed support, and case-sensitive + if (window.MSPointerEvent) { + POINTER_ELEMENT_EVENTS = 'MSPointerDown'; + POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; + } + + /** + * Pointer events input + * @constructor + * @extends Input + */ + function PointerEventInput() { + this.evEl = POINTER_ELEMENT_EVENTS; + this.evWin = POINTER_WINDOW_EVENTS; + + Input.apply(this, arguments); + + this.store = (this.manager.session.pointerEvents = []); + } + + inherit(PointerEventInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function PEhandler(ev) { + var store = this.store; + var removePointer = false; + + var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); + var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; + var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; + + var isTouch = (pointerType == INPUT_TYPE_TOUCH); + + // get index of the event in the store + var storeIndex = inArray(store, ev.pointerId, 'pointerId'); + + // start and mouse must be down + if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { + if (storeIndex < 0) { + store.push(ev); + storeIndex = store.length - 1; + } + } else if (eventType & (INPUT_END | INPUT_CANCEL)) { + removePointer = true; + } + + // it not found, so the pointer hasn't been down (so it's probably a hover) + if (storeIndex < 0) { + return; + } + + // update the event in the store + store[storeIndex] = ev; + + this.callback(this.manager, eventType, { + pointers: store, + changedPointers: [ev], + pointerType: pointerType, + srcEvent: ev + }); + + if (removePointer) { + // remove from the store + store.splice(storeIndex, 1); + } + } + }); + + var SINGLE_TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL + }; + + var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; + var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; + + /** + * Touch events input + * @constructor + * @extends Input + */ + function SingleTouchInput() { + this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; + this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; + this.started = false; + + Input.apply(this, arguments); + } + + inherit(SingleTouchInput, Input, { + handler: function TEhandler(ev) { + var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; + + // should we handle the touch events? + if (type === INPUT_START) { + this.started = true; + } + + if (!this.started) { + return; + } + + var touches = normalizeSingleTouches.call(this, ev, type); + + // when done, reset the started state + if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { + this.started = false; + } + + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); + } + }); + + /** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ + function normalizeSingleTouches(ev, type) { + var all = toArray(ev.touches); + var changed = toArray(ev.changedTouches); + + if (type & (INPUT_END | INPUT_CANCEL)) { + all = uniqueArray(all.concat(changed), 'identifier', true); + } + + return [all, changed]; + } + + var TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL + }; + + var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; + + /** + * Multi-user touch events input + * @constructor + * @extends Input + */ + function TouchInput() { + this.evTarget = TOUCH_TARGET_EVENTS; + this.targetIds = {}; + + Input.apply(this, arguments); + } + + inherit(TouchInput, Input, { + handler: function MTEhandler(ev) { + var type = TOUCH_INPUT_MAP[ev.type]; + var touches = getTouches.call(this, ev, type); + if (!touches) { + return; + } + + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); + } + }); + + /** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ + function getTouches(ev, type) { + var allTouches = toArray(ev.touches); + var targetIds = this.targetIds; + + // when there is only one touch, the process can be simplified + if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { + targetIds[allTouches[0].identifier] = true; + return [allTouches, allTouches]; + } + + var i, + targetTouches, + changedTouches = toArray(ev.changedTouches), + changedTargetTouches = [], + target = this.target; + + // get target touches from touches + targetTouches = allTouches.filter(function(touch) { + return hasParent(touch.target, target); + }); + + // collect touches + if (type === INPUT_START) { + i = 0; + while (i < targetTouches.length) { + targetIds[targetTouches[i].identifier] = true; + i++; + } + } + + // filter changed touches to only contain touches that exist in the collected target ids + i = 0; + while (i < changedTouches.length) { + if (targetIds[changedTouches[i].identifier]) { + changedTargetTouches.push(changedTouches[i]); + } + + // cleanup removed touches + if (type & (INPUT_END | INPUT_CANCEL)) { + delete targetIds[changedTouches[i].identifier]; + } + i++; + } + + if (!changedTargetTouches.length) { + return; + } + + return [ + // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' + uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), + changedTargetTouches + ]; + } + + /** + * Combined touch and mouse input + * + * Touch has a higher priority then mouse, and while touching no mouse events are allowed. + * This because touch devices also emit mouse events while doing a touch. + * + * @constructor + * @extends Input + */ + function TouchMouseInput() { + Input.apply(this, arguments); + + var handler = bindFn(this.handler, this); + this.touch = new TouchInput(this.manager, handler); + this.mouse = new MouseInput(this.manager, handler); + } + + inherit(TouchMouseInput, Input, { + /** + * handle mouse and touch events + * @param {Hammer} manager + * @param {String} inputEvent + * @param {Object} inputData + */ + handler: function TMEhandler(manager, inputEvent, inputData) { + var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), + isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); + + // when we're in a touch event, so block all upcoming mouse events + // most mobile browser also emit mouseevents, right after touchstart + if (isTouch) { + this.mouse.allow = false; + } else if (isMouse && !this.mouse.allow) { + return; + } + + // reset the allowMouse when we're done + if (inputEvent & (INPUT_END | INPUT_CANCEL)) { + this.mouse.allow = true; + } + + this.callback(manager, inputEvent, inputData); + }, + + /** + * remove the event listeners + */ + destroy: function destroy() { + this.touch.destroy(); + this.mouse.destroy(); + } + }); + + var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); + var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; + + // magical touchAction value + var TOUCH_ACTION_COMPUTE = 'compute'; + var TOUCH_ACTION_AUTO = 'auto'; + var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented + var TOUCH_ACTION_NONE = 'none'; + var TOUCH_ACTION_PAN_X = 'pan-x'; + var TOUCH_ACTION_PAN_Y = 'pan-y'; + + /** + * Touch Action + * sets the touchAction property or uses the js alternative + * @param {Manager} manager + * @param {String} value + * @constructor + */ + function TouchAction(manager, value) { + this.manager = manager; + this.set(value); + } + + TouchAction.prototype = { + /** + * set the touchAction value on the element or enable the polyfill + * @param {String} value + */ + set: function(value) { + // find out the touch-action by the event handlers + if (value == TOUCH_ACTION_COMPUTE) { + value = this.compute(); + } + + if (NATIVE_TOUCH_ACTION) { + this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; + } + this.actions = value.toLowerCase().trim(); + }, + + /** + * just re-set the touchAction value + */ + update: function() { + this.set(this.manager.options.touchAction); + }, + + /** + * compute the value for the touchAction property based on the recognizer's settings + * @returns {String} value + */ + compute: function() { + var actions = []; + each(this.manager.recognizers, function(recognizer) { + if (boolOrFn(recognizer.options.enable, [recognizer])) { + actions = actions.concat(recognizer.getTouchAction()); + } + }); + return cleanTouchActions(actions.join(' ')); + }, + + /** + * this method is called on each input cycle and provides the preventing of the browser behavior + * @param {Object} input + */ + preventDefaults: function(input) { + // not needed with native support for the touchAction property + if (NATIVE_TOUCH_ACTION) { + return; + } + + var srcEvent = input.srcEvent; + var direction = input.offsetDirection; + + // if the touch action did prevented once this session + if (this.manager.session.prevented) { + srcEvent.preventDefault(); + return; + } + + var actions = this.actions; + var hasNone = inStr(actions, TOUCH_ACTION_NONE); + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); + + if (hasNone || + (hasPanY && direction & DIRECTION_HORIZONTAL) || + (hasPanX && direction & DIRECTION_VERTICAL)) { + return this.preventSrc(srcEvent); + } + }, + + /** + * call preventDefault to prevent the browser's default behavior (scrolling in most cases) + * @param {Object} srcEvent + */ + preventSrc: function(srcEvent) { + this.manager.session.prevented = true; + srcEvent.preventDefault(); + } + }; + + /** + * when the touchActions are collected they are not a valid value, so we need to clean things up. * + * @param {String} actions + * @returns {*} + */ + function cleanTouchActions(actions) { + // none + if (inStr(actions, TOUCH_ACTION_NONE)) { + return TOUCH_ACTION_NONE; + } + + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); + + // pan-x and pan-y can be combined + if (hasPanX && hasPanY) { + return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y; + } + + // pan-x OR pan-y + if (hasPanX || hasPanY) { + return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; + } + + // manipulation + if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { + return TOUCH_ACTION_MANIPULATION; + } + + return TOUCH_ACTION_AUTO; + } + + /** + * Recognizer flow explained; * + * All recognizers have the initial state of POSSIBLE when a input session starts. + * The definition of a input session is from the first input until the last input, with all it's movement in it. * + * Example session for mouse-input: mousedown -> mousemove -> mouseup + * + * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed + * which determines with state it should be. + * + * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to + * POSSIBLE to give it another change on the next cycle. + * + * Possible + * | + * +-----+---------------+ + * | | + * +-----+-----+ | + * | | | + * Failed Cancelled | + * +-------+------+ + * | | + * Recognized Began + * | + * Changed + * | + * Ended/Recognized + */ + var STATE_POSSIBLE = 1; + var STATE_BEGAN = 2; + var STATE_CHANGED = 4; + var STATE_ENDED = 8; + var STATE_RECOGNIZED = STATE_ENDED; + var STATE_CANCELLED = 16; + var STATE_FAILED = 32; + + /** + * Recognizer + * Every recognizer needs to extend from this class. + * @constructor + * @param {Object} options + */ + function Recognizer(options) { + this.id = uniqueId(); + + this.manager = null; + this.options = merge(options || {}, this.defaults); + + // default is enable true + this.options.enable = ifUndefined(this.options.enable, true); + + this.state = STATE_POSSIBLE; + + this.simultaneous = {}; + this.requireFail = []; + } + + Recognizer.prototype = { + /** + * @virtual + * @type {Object} + */ + defaults: {}, + + /** + * set options + * @param {Object} options + * @return {Recognizer} + */ + set: function(options) { + extend(this.options, options); + + // also update the touchAction, in case something changed about the directions/enabled state + this.manager && this.manager.touchAction.update(); + return this; + }, + + /** + * recognize simultaneous with an other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + recognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { + return this; + } + + var simultaneous = this.simultaneous; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (!simultaneous[otherRecognizer.id]) { + simultaneous[otherRecognizer.id] = otherRecognizer; + otherRecognizer.recognizeWith(this); + } + return this; + }, + + /** + * drop the simultaneous link. it doesnt remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRecognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { + return this; + } + + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + delete this.simultaneous[otherRecognizer.id]; + return this; + }, + + /** + * recognizer can only run when an other is failing + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + requireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { + return this; + } + + var requireFail = this.requireFail; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (inArray(requireFail, otherRecognizer) === -1) { + requireFail.push(otherRecognizer); + otherRecognizer.requireFailure(this); + } + return this; + }, + + /** + * drop the requireFailure link. it does not remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRequireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { + return this; + } + + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + var index = inArray(this.requireFail, otherRecognizer); + if (index > -1) { + this.requireFail.splice(index, 1); + } + return this; + }, + + /** + * has require failures boolean + * @returns {boolean} + */ + hasRequireFailures: function() { + return this.requireFail.length > 0; + }, + + /** + * if the recognizer can recognize simultaneous with an other recognizer + * @param {Recognizer} otherRecognizer + * @returns {Boolean} + */ + canRecognizeWith: function(otherRecognizer) { + return !!this.simultaneous[otherRecognizer.id]; + }, + + /** + * You should use `tryEmit` instead of `emit` directly to check + * that all the needed recognizers has failed before emitting. + * @param {Object} input + */ + emit: function(input) { + var self = this; + var state = this.state; + + function emit(withState) { + self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input); + } + + // 'panstart' and 'panmove' + if (state < STATE_ENDED) { + emit(true); + } + + emit(); // simple 'eventName' events + + // panend and pancancel + if (state >= STATE_ENDED) { + emit(true); + } + }, + + /** + * Check that all the require failure recognizers has failed, + * if true, it emits a gesture event, + * otherwise, setup the state to FAILED. + * @param {Object} input + */ + tryEmit: function(input) { + if (this.canEmit()) { + return this.emit(input); + } + // it's failing anyway + this.state = STATE_FAILED; + }, + + /** + * can we emit? + * @returns {boolean} + */ + canEmit: function() { + var i = 0; + while (i < this.requireFail.length) { + if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { + return false; + } + i++; + } + return true; + }, + + /** + * update the recognizer + * @param {Object} inputData + */ + recognize: function(inputData) { + // make a new copy of the inputData + // so we can change the inputData without messing up the other recognizers + var inputDataClone = extend({}, inputData); + + // is is enabled and allow recognizing? + if (!boolOrFn(this.options.enable, [this, inputDataClone])) { + this.reset(); + this.state = STATE_FAILED; + return; + } + + // reset when we've reached the end + if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { + this.state = STATE_POSSIBLE; + } + + this.state = this.process(inputDataClone); + + // the recognizer has recognized a gesture + // so trigger an event + if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { + this.tryEmit(inputDataClone); + } + }, + + /** + * return the state of the recognizer + * the actual recognizing happens in this method + * @virtual + * @param {Object} inputData + * @returns {Const} STATE + */ + process: function(inputData) { }, // jshint ignore:line + + /** + * return the preferred touch-action + * @virtual + * @returns {Array} + */ + getTouchAction: function() { }, + + /** + * called when the gesture isn't allowed to recognize + * like when another is being recognized or it is disabled + * @virtual + */ + reset: function() { } + }; + + /** + * get a usable string, used as event postfix + * @param {Const} state + * @returns {String} state + */ + function stateStr(state) { + if (state & STATE_CANCELLED) { + return 'cancel'; + } else if (state & STATE_ENDED) { + return 'end'; + } else if (state & STATE_CHANGED) { + return 'move'; + } else if (state & STATE_BEGAN) { + return 'start'; + } + return ''; + } + + /** + * direction cons to string + * @param {Const} direction + * @returns {String} + */ + function directionStr(direction) { + if (direction == DIRECTION_DOWN) { + return 'down'; + } else if (direction == DIRECTION_UP) { + return 'up'; + } else if (direction == DIRECTION_LEFT) { + return 'left'; + } else if (direction == DIRECTION_RIGHT) { + return 'right'; + } + return ''; + } + + /** + * get a recognizer by name if it is bound to a manager + * @param {Recognizer|String} otherRecognizer + * @param {Recognizer} recognizer + * @returns {Recognizer} + */ + function getRecognizerByNameIfManager(otherRecognizer, recognizer) { + var manager = recognizer.manager; + if (manager) { + return manager.get(otherRecognizer); + } + return otherRecognizer; + } + + /** + * This recognizer is just used as a base for the simple attribute recognizers. + * @constructor + * @extends Recognizer + */ + function AttrRecognizer() { + Recognizer.apply(this, arguments); + } + + inherit(AttrRecognizer, Recognizer, { + /** + * @namespace + * @memberof AttrRecognizer + */ + defaults: { + /** + * @type {Number} + * @default 1 + */ + pointers: 1 + }, + + /** + * Used to check if it the recognizer receives valid input, like input.distance > 10. + * @memberof AttrRecognizer + * @param {Object} input + * @returns {Boolean} recognized + */ + attrTest: function(input) { + var optionPointers = this.options.pointers; + return optionPointers === 0 || input.pointers.length === optionPointers; + }, + + /** + * Process the input and return the state for the recognizer + * @memberof AttrRecognizer + * @param {Object} input + * @returns {*} State + */ + process: function(input) { + var state = this.state; + var eventType = input.eventType; + + var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); + var isValid = this.attrTest(input); + + // on cancel input and we've recognized before, return STATE_CANCELLED + if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { + return state | STATE_CANCELLED; + } else if (isRecognized || isValid) { + if (eventType & INPUT_END) { + return state | STATE_ENDED; + } else if (!(state & STATE_BEGAN)) { + return STATE_BEGAN; + } + return state | STATE_CHANGED; + } + return STATE_FAILED; + } + }); + + /** + * Pan + * Recognized when the pointer is down and moved in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ + function PanRecognizer() { + AttrRecognizer.apply(this, arguments); + + this.pX = null; + this.pY = null; + } + + inherit(PanRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PanRecognizer + */ + defaults: { + event: 'pan', + threshold: 10, + pointers: 1, + direction: DIRECTION_ALL + }, + + getTouchAction: function() { + var direction = this.options.direction; + var actions = []; + if (direction & DIRECTION_HORIZONTAL) { + actions.push(TOUCH_ACTION_PAN_Y); + } + if (direction & DIRECTION_VERTICAL) { + actions.push(TOUCH_ACTION_PAN_X); + } + return actions; + }, + + directionTest: function(input) { + var options = this.options; + var hasMoved = true; + var distance = input.distance; + var direction = input.direction; + var x = input.deltaX; + var y = input.deltaY; + + // lock to axis? + if (!(direction & options.direction)) { + if (options.direction & DIRECTION_HORIZONTAL) { + direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; + hasMoved = x != this.pX; + distance = Math.abs(input.deltaX); + } else { + direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; + hasMoved = y != this.pY; + distance = Math.abs(input.deltaY); + } + } + input.direction = direction; + return hasMoved && distance > options.threshold && direction & options.direction; + }, + + attrTest: function(input) { + return AttrRecognizer.prototype.attrTest.call(this, input) && + (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); + }, + + emit: function(input) { + this.pX = input.deltaX; + this.pY = input.deltaY; + + var direction = directionStr(input.direction); + if (direction) { + this.manager.emit(this.options.event + direction, input); + } + + this._super.emit.call(this, input); + } + }); + + /** + * Pinch + * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). + * @constructor + * @extends AttrRecognizer + */ + function PinchRecognizer() { + AttrRecognizer.apply(this, arguments); + } + + inherit(PinchRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'pinch', + threshold: 0, + pointers: 2 + }, + + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, + + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); + }, + + emit: function(input) { + this._super.emit.call(this, input); + if (input.scale !== 1) { + var inOut = input.scale < 1 ? 'in' : 'out'; + this.manager.emit(this.options.event + inOut, input); + } + } + }); + + /** + * Press + * Recognized when the pointer is down for x ms without any movement. + * @constructor + * @extends Recognizer + */ + function PressRecognizer() { + Recognizer.apply(this, arguments); + + this._timer = null; + this._input = null; + } + + inherit(PressRecognizer, Recognizer, { + /** + * @namespace + * @memberof PressRecognizer + */ + defaults: { + event: 'press', + pointers: 1, + time: 500, // minimal time of the pointer to be pressed + threshold: 5 // a minimal movement is ok, but keep it low + }, + + getTouchAction: function() { + return [TOUCH_ACTION_AUTO]; + }, + + process: function(input) { + var options = this.options; + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTime = input.deltaTime > options.time; + + this._input = input; + + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { + this.reset(); + } else if (input.eventType & INPUT_START) { + this.reset(); + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.time, this); + } else if (input.eventType & INPUT_END) { + return STATE_RECOGNIZED; + } + return STATE_FAILED; + }, + + reset: function() { + clearTimeout(this._timer); + }, + + emit: function(input) { + if (this.state !== STATE_RECOGNIZED) { + return; + } + + if (input && (input.eventType & INPUT_END)) { + this.manager.emit(this.options.event + 'up', input); + } else { + this._input.timeStamp = now(); + this.manager.emit(this.options.event, this._input); + } + } + }); + + /** + * Rotate + * Recognized when two or more pointer are moving in a circular motion. + * @constructor + * @extends AttrRecognizer + */ + function RotateRecognizer() { + AttrRecognizer.apply(this, arguments); + } + + inherit(RotateRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof RotateRecognizer + */ + defaults: { + event: 'rotate', + threshold: 0, + pointers: 2 + }, + + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, + + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); + } + }); + + /** + * Swipe + * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ + function SwipeRecognizer() { + AttrRecognizer.apply(this, arguments); + } + + inherit(SwipeRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof SwipeRecognizer + */ + defaults: { + event: 'swipe', + threshold: 10, + velocity: 0.65, + direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, + pointers: 1 + }, + + getTouchAction: function() { + return PanRecognizer.prototype.getTouchAction.call(this); + }, + + attrTest: function(input) { + var direction = this.options.direction; + var velocity; + + if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { + velocity = input.velocity; + } else if (direction & DIRECTION_HORIZONTAL) { + velocity = input.velocityX; + } else if (direction & DIRECTION_VERTICAL) { + velocity = input.velocityY; + } + + return this._super.attrTest.call(this, input) && + direction & input.direction && + input.distance > this.options.threshold && + abs(velocity) > this.options.velocity && input.eventType & INPUT_END; + }, + + emit: function(input) { + var direction = directionStr(input.direction); + if (direction) { + this.manager.emit(this.options.event + direction, input); + } + + this.manager.emit(this.options.event, input); + } + }); + + /** + * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur + * between the given interval and position. The delay option can be used to recognize multi-taps without firing + * a single tap. + * + * The eventData from the emitted event contains the property `tapCount`, which contains the amount of + * multi-taps being recognized. + * @constructor + * @extends Recognizer + */ + function TapRecognizer() { + Recognizer.apply(this, arguments); + + // previous time and center, + // used for tap counting + this.pTime = false; + this.pCenter = false; + + this._timer = null; + this._input = null; + this.count = 0; + } + + inherit(TapRecognizer, Recognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'tap', + pointers: 1, + taps: 1, + interval: 300, // max time between the multi-tap taps + time: 250, // max time of the pointer to be down (like finger on the screen) + threshold: 2, // a minimal movement is ok, but keep it low + posThreshold: 10 // a multi-tap can be a bit off the initial position + }, + + getTouchAction: function() { + return [TOUCH_ACTION_MANIPULATION]; + }, + + process: function(input) { + var options = this.options; + + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTouchTime = input.deltaTime < options.time; + + this.reset(); + + if ((input.eventType & INPUT_START) && (this.count === 0)) { + return this.failTimeout(); + } + + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (validMovement && validTouchTime && validPointers) { + if (input.eventType != INPUT_END) { + return this.failTimeout(); + } + + var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; + var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; + + this.pTime = input.timeStamp; + this.pCenter = input.center; + + if (!validMultiTap || !validInterval) { + this.count = 1; + } else { + this.count += 1; + } + + this._input = input; + + // if tap count matches we have recognized it, + // else it has began recognizing... + var tapCount = this.count % options.taps; + if (tapCount === 0) { + // no failing requirements, immediately trigger the tap event + // or wait as long as the multitap interval to trigger + if (!this.hasRequireFailures()) { + return STATE_RECOGNIZED; + } else { + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.interval, this); + return STATE_BEGAN; + } + } + } + return STATE_FAILED; + }, + + failTimeout: function() { + this._timer = setTimeoutContext(function() { + this.state = STATE_FAILED; + }, this.options.interval, this); + return STATE_FAILED; + }, + + reset: function() { + clearTimeout(this._timer); + }, + + emit: function() { + if (this.state == STATE_RECOGNIZED ) { + this._input.tapCount = this.count; + this.manager.emit(this.options.event, this._input); + } + } + }); + + /** + * Simple way to create an manager with a default set of recognizers. + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ + function Hammer(element, options) { + options = options || {}; + options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); + return new Manager(element, options); + } + + /** + * @const {string} + */ + Hammer.VERSION = '2.0.4'; + + /** + * default settings + * @namespace + */ + Hammer.defaults = { + /** + * set if DOM events are being triggered. + * But this is slower and unused by simple implementations, so disabled by default. + * @type {Boolean} + * @default false + */ + domEvents: false, + + /** + * The value for the touchAction property/fallback. + * When set to `compute` it will magically set the correct value based on the added recognizers. + * @type {String} + * @default compute + */ + touchAction: TOUCH_ACTION_COMPUTE, + + /** + * @type {Boolean} + * @default true + */ + enable: true, + + /** + * EXPERIMENTAL FEATURE -- can be removed/changed + * Change the parent input target element. + * If Null, then it is being set the to main element. + * @type {Null|EventTarget} + * @default null + */ + inputTarget: null, + + /** + * force an input class + * @type {Null|Function} + * @default null + */ + inputClass: null, + + /** + * Default recognizer setup when calling `Hammer()` + * When creating a new Manager these will be skipped. + * @type {Array} + */ + preset: [ + // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] + [RotateRecognizer, { enable: false }], + [PinchRecognizer, { enable: false }, ['rotate']], + [SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }], + [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], + [TapRecognizer], + [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], + [PressRecognizer] + ], + + /** + * Some CSS properties can be used to improve the working of Hammer. + * Add them to this method and they will be set when creating a new Manager. + * @namespace + */ + cssProps: { + /** + * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userSelect: 'none', + + /** + * Disable the Windows Phone grippers when pressing an element. + * @type {String} + * @default 'none' + */ + touchSelect: 'none', + + /** + * Disables the default callout shown when you touch and hold a touch target. + * On iOS, when you touch and hold a touch target such as a link, Safari displays + * a callout containing information about the link. This property allows you to disable that callout. + * @type {String} + * @default 'none' + */ + touchCallout: 'none', + + /** + * Specifies whether zooming is enabled. Used by IE10> + * @type {String} + * @default 'none' + */ + contentZooming: 'none', + + /** + * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userDrag: 'none', + + /** + * Overrides the highlight color shown when the user taps a link or a JavaScript + * clickable element in iOS. This property obeys the alpha value, if specified. + * @type {String} + * @default 'rgba(0,0,0,0)' + */ + tapHighlightColor: 'rgba(0,0,0,0)' + } + }; + + var STOP = 1; + var FORCED_STOP = 2; + + /** + * Manager + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ + function Manager(element, options) { + options = options || {}; + + this.options = merge(options, Hammer.defaults); + this.options.inputTarget = this.options.inputTarget || element; + + this.handlers = {}; + this.session = {}; + this.recognizers = []; + + this.element = element; + this.input = createInputInstance(this); + this.touchAction = new TouchAction(this, this.options.touchAction); + + toggleCssProps(this, true); + + each(options.recognizers, function(item) { + var recognizer = this.add(new (item[0])(item[1])); + item[2] && recognizer.recognizeWith(item[2]); + item[3] && recognizer.requireFailure(item[3]); + }, this); + } + + Manager.prototype = { + /** + * set options + * @param {Object} options + * @returns {Manager} + */ + set: function(options) { + extend(this.options, options); + + // Options that need a little more setup + if (options.touchAction) { + this.touchAction.update(); + } + if (options.inputTarget) { + // Clean up existing event listeners and reinitialize + this.input.destroy(); + this.input.target = options.inputTarget; + this.input.init(); + } + return this; + }, + + /** + * stop recognizing for this session. + * This session will be discarded, when a new [input]start event is fired. + * When forced, the recognizer cycle is stopped immediately. + * @param {Boolean} [force] + */ + stop: function(force) { + this.session.stopped = force ? FORCED_STOP : STOP; + }, + + /** + * run the recognizers! + * called by the inputHandler function on every movement of the pointers (touches) + * it walks through all the recognizers and tries to detect the gesture that is being made + * @param {Object} inputData + */ + recognize: function(inputData) { + var session = this.session; + if (session.stopped) { + return; + } + + // run the touch-action polyfill + this.touchAction.preventDefaults(inputData); + + var recognizer; + var recognizers = this.recognizers; + + // this holds the recognizer that is being recognized. + // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED + // if no recognizer is detecting a thing, it is set to `null` + var curRecognizer = session.curRecognizer; + + // reset when the last recognizer is recognized + // or when we're in a new session + if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { + curRecognizer = session.curRecognizer = null; + } + + var i = 0; + while (i < recognizers.length) { + recognizer = recognizers[i]; + + // find out if we are allowed try to recognize the input for this one. + // 1. allow if the session is NOT forced stopped (see the .stop() method) + // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one + // that is being recognized. + // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. + // this can be setup with the `recognizeWith()` method on the recognizer. + if (session.stopped !== FORCED_STOP && ( // 1 + !curRecognizer || recognizer == curRecognizer || // 2 + recognizer.canRecognizeWith(curRecognizer))) { // 3 + recognizer.recognize(inputData); + } else { + recognizer.reset(); + } + + // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the + // current active recognizer. but only if we don't already have an active recognizer + if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { + curRecognizer = session.curRecognizer = recognizer; + } + i++; + } + }, + + /** + * get a recognizer by its event name. + * @param {Recognizer|String} recognizer + * @returns {Recognizer|Null} + */ + get: function(recognizer) { + if (recognizer instanceof Recognizer) { + return recognizer; + } + + var recognizers = this.recognizers; + for (var i = 0; i < recognizers.length; i++) { + if (recognizers[i].options.event == recognizer) { + return recognizers[i]; + } + } + return null; + }, + + /** + * add a recognizer to the manager + * existing recognizers with the same event name will be removed + * @param {Recognizer} recognizer + * @returns {Recognizer|Manager} + */ + add: function(recognizer) { + if (invokeArrayArg(recognizer, 'add', this)) { + return this; + } + + // remove existing + var existing = this.get(recognizer.options.event); + if (existing) { + this.remove(existing); + } + + this.recognizers.push(recognizer); + recognizer.manager = this; + + this.touchAction.update(); + return recognizer; + }, + + /** + * remove a recognizer by name or instance + * @param {Recognizer|String} recognizer + * @returns {Manager} + */ + remove: function(recognizer) { + if (invokeArrayArg(recognizer, 'remove', this)) { + return this; + } + + var recognizers = this.recognizers; + recognizer = this.get(recognizer); + recognizers.splice(inArray(recognizers, recognizer), 1); + + this.touchAction.update(); + return this; + }, + + /** + * bind event + * @param {String} events + * @param {Function} handler + * @returns {EventEmitter} this + */ + on: function(events, handler) { + var handlers = this.handlers; + each(splitStr(events), function(event) { + handlers[event] = handlers[event] || []; + handlers[event].push(handler); + }); + return this; + }, + + /** + * unbind event, leave emit blank to remove all handlers + * @param {String} events + * @param {Function} [handler] + * @returns {EventEmitter} this + */ + off: function(events, handler) { + var handlers = this.handlers; + each(splitStr(events), function(event) { + if (!handler) { + delete handlers[event]; + } else { + handlers[event].splice(inArray(handlers[event], handler), 1); + } + }); + return this; + }, + + /** + * emit event to the listeners + * @param {String} event + * @param {Object} data + */ + emit: function(event, data) { + // we also want to trigger dom events + if (this.options.domEvents) { + triggerDomEvent(event, data); + } + + // no handlers, so skip it all + var handlers = this.handlers[event] && this.handlers[event].slice(); + if (!handlers || !handlers.length) { + return; + } + + data.type = event; + data.preventDefault = function() { + data.srcEvent.preventDefault(); + }; + + var i = 0; + while (i < handlers.length) { + handlers[i](data); + i++; + } + }, + + /** + * destroy the manager and unbinds all events + * it doesn't unbind dom events, that is the user own responsibility + */ + destroy: function() { + this.element && toggleCssProps(this, false); + + this.handlers = {}; + this.session = {}; + this.input.destroy(); + this.element = null; + } + }; + + /** + * add/remove the css properties as defined in manager.options.cssProps + * @param {Manager} manager + * @param {Boolean} add + */ + function toggleCssProps(manager, add) { + var element = manager.element; + each(manager.options.cssProps, function(value, name) { + element.style[prefixed(element.style, name)] = add ? value : ''; + }); + } + + /** + * trigger dom event + * @param {String} event + * @param {Object} data + */ + function triggerDomEvent(event, data) { + var gestureEvent = document.createEvent('Event'); + gestureEvent.initEvent(event, true, true); + gestureEvent.gesture = data; + data.target.dispatchEvent(gestureEvent); + } + + extend(Hammer, { + INPUT_START: INPUT_START, + INPUT_MOVE: INPUT_MOVE, + INPUT_END: INPUT_END, + INPUT_CANCEL: INPUT_CANCEL, + + STATE_POSSIBLE: STATE_POSSIBLE, + STATE_BEGAN: STATE_BEGAN, + STATE_CHANGED: STATE_CHANGED, + STATE_ENDED: STATE_ENDED, + STATE_RECOGNIZED: STATE_RECOGNIZED, + STATE_CANCELLED: STATE_CANCELLED, + STATE_FAILED: STATE_FAILED, + + DIRECTION_NONE: DIRECTION_NONE, + DIRECTION_LEFT: DIRECTION_LEFT, + DIRECTION_RIGHT: DIRECTION_RIGHT, + DIRECTION_UP: DIRECTION_UP, + DIRECTION_DOWN: DIRECTION_DOWN, + DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, + DIRECTION_VERTICAL: DIRECTION_VERTICAL, + DIRECTION_ALL: DIRECTION_ALL, + + Manager: Manager, + Input: Input, + TouchAction: TouchAction, + + TouchInput: TouchInput, + MouseInput: MouseInput, + PointerEventInput: PointerEventInput, + TouchMouseInput: TouchMouseInput, + SingleTouchInput: SingleTouchInput, + + Recognizer: Recognizer, + AttrRecognizer: AttrRecognizer, + Tap: TapRecognizer, + Pan: PanRecognizer, + Swipe: SwipeRecognizer, + Pinch: PinchRecognizer, + Rotate: RotateRecognizer, + Press: PressRecognizer, + + on: addEventListeners, + off: removeEventListeners, + each: each, + merge: merge, + extend: extend, + inherit: inherit, + bindFn: bindFn, + prefixed: prefixed + }); + + if ("function" == TYPE_FUNCTION && __webpack_require__(23)) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { + return Hammer; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof module != 'undefined' && module.exports) { + module.exports = Hammer; + } else { + window[exportName] = Hammer; + } + + })(window, document, 'Hammer'); + + +/***/ }, +/* 23 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; + + /* WEBPACK VAR INJECTION */}.call(exports, {})) + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var util = __webpack_require__(1); + var hammerUtil = __webpack_require__(25); + var moment = __webpack_require__(2); + var Component = __webpack_require__(26); + var DateUtil = __webpack_require__(27); + + /** + * @constructor Range + * A Range controls a numeric range with a start and end value. + * The Range adjusts the range based on mouse events or programmatic changes, + * and triggers events when the range is changing or has been changed. + * @param {{dom: Object, domProps: Object, emitter: Emitter}} body + * @param {Object} [options] See description at Range.setOptions + */ + function Range(body, options) { + var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); + this.start = now.clone().add(-3, 'days').valueOf(); // Number + this.end = now.clone().add(4, 'days').valueOf(); // Number + + this.body = body; + this.deltaDifference = 0; + this.scaleOffset = 0; + this.startToFront = false; + this.endToFront = true; + + // default options + this.defaultOptions = { + start: null, + end: null, + moment: moment, + direction: 'horizontal', // 'horizontal' or 'vertical' + moveable: true, + zoomable: true, + min: null, + max: null, + zoomMin: 10, // milliseconds + zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds + }; + this.options = util.extend({}, this.defaultOptions); + + this.props = { + touch: {} + }; + this.animationTimer = null; + + // drag listeners for dragging + this.body.emitter.on('panstart', this._onDragStart.bind(this)); + this.body.emitter.on('panmove', this._onDrag.bind(this)); + this.body.emitter.on('panend', this._onDragEnd.bind(this)); + + // mouse wheel for zooming + this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); + + // pinch to zoom + this.body.emitter.on('touch', this._onTouch.bind(this)); + this.body.emitter.on('pinch', this._onPinch.bind(this)); + + this.setOptions(options); + } + + Range.prototype = new Component(); + + /** + * Set options for the range controller + * @param {Object} options Available options: + * {Number | Date | String} start Start date for the range + * {Number | Date | String} end End date for the range + * {Number} min Minimum value for start + * {Number} max Maximum value for end + * {Number} zoomMin Set a minimum value for + * (end - start). + * {Number} zoomMax Set a maximum value for + * (end - start). + * {Boolean} moveable Enable moving of the range + * by dragging. True by default + * {Boolean} zoomable Enable zooming of the range + * by pinching/scrolling. True by default + */ + Range.prototype.setOptions = function (options) { + if (options) { + // copy the options that we know + var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey']; + util.selectiveExtend(fields, this.options, options); + + if ('start' in options || 'end' in options) { + // apply a new range. both start and end are optional + this.setRange(options.start, options.end); + } + } + }; + + /** + * Test whether direction has a valid value + * @param {String} direction 'horizontal' or 'vertical' + */ + function validateDirection(direction) { + if (direction != 'horizontal' && direction != 'vertical') { + throw new TypeError('Unknown direction "' + direction + '". ' + 'Choose "horizontal" or "vertical".'); + } + } + + /** + * Set a new start and end range + * @param {Date | Number | String} [start] + * @param {Date | Number | String} [end] + * @param {boolean | {duration: number, easingFunction: string}} [animation=false] + * If true (default), the range is animated + * smoothly to the new window. An object can be + * provided to specify duration and easing function. + * Default duration is 500 ms, and default easing + * function is 'easeInOutQuad'. + * @param {Boolean} [byUser=false] + * + */ + Range.prototype.setRange = function (start, end, animation, byUser) { + if (byUser !== true) { + byUser = false; + } + var finalStart = start != undefined ? util.convert(start, 'Date').valueOf() : null; + var finalEnd = end != undefined ? util.convert(end, 'Date').valueOf() : null; + this._cancelAnimation(); + + if (animation) { + // true or an Object + var me = this; + var initStart = this.start; + var initEnd = this.end; + var duration = typeof animation === 'object' && 'duration' in animation ? animation.duration : 500; + var easingName = typeof animation === 'object' && 'easingFunction' in animation ? animation.easingFunction : 'easeInOutQuad'; + var easingFunction = util.easingFunctions[easingName]; + if (!easingFunction) { + throw new Error('Unknown easing function ' + JSON.stringify(easingName) + '. ' + 'Choose from: ' + Object.keys(util.easingFunctions).join(', ')); + } + + var initTime = new Date().valueOf(); + var anyChanged = false; + + var next = function next() { + if (!me.props.touch.dragging) { + var now = new Date().valueOf(); + var time = now - initTime; + var ease = easingFunction(time / duration); + var done = time > duration; + var s = done || finalStart === null ? finalStart : initStart + (finalStart - initStart) * ease; + var e = done || finalEnd === null ? finalEnd : initEnd + (finalEnd - initEnd) * ease; + + changed = me._applyRange(s, e); + DateUtil.updateHiddenDates(me.options.moment, me.body, me.options.hiddenDates); + anyChanged = anyChanged || changed; + if (changed) { + me.body.emitter.emit('rangechange', { start: new Date(me.start), end: new Date(me.end), byUser: byUser }); + } + + if (done) { + if (anyChanged) { + me.body.emitter.emit('rangechanged', { start: new Date(me.start), end: new Date(me.end), byUser: byUser }); + } + } else { + // animate with as high as possible frame rate, leave 20 ms in between + // each to prevent the browser from blocking + me.animationTimer = setTimeout(next, 20); + } + } + }; + + return next(); + } else { + var changed = this._applyRange(finalStart, finalEnd); + DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates); + if (changed) { + var params = { start: new Date(this.start), end: new Date(this.end), byUser: byUser }; + this.body.emitter.emit('rangechange', params); + this.body.emitter.emit('rangechanged', params); + } + } + }; + + /** + * Stop an animation + * @private + */ + Range.prototype._cancelAnimation = function () { + if (this.animationTimer) { + clearTimeout(this.animationTimer); + this.animationTimer = null; + } + }; + + /** + * Set a new start and end range. This method is the same as setRange, but + * does not trigger a range change and range changed event, and it returns + * true when the range is changed + * @param {Number} [start] + * @param {Number} [end] + * @return {Boolean} changed + * @private + */ + Range.prototype._applyRange = function (start, end) { + var newStart = start != null ? util.convert(start, 'Date').valueOf() : this.start, + newEnd = end != null ? util.convert(end, 'Date').valueOf() : this.end, + max = this.options.max != null ? util.convert(this.options.max, 'Date').valueOf() : null, + min = this.options.min != null ? util.convert(this.options.min, 'Date').valueOf() : null, + diff; + + // check for valid number + if (isNaN(newStart) || newStart === null) { + throw new Error('Invalid start "' + start + '"'); + } + if (isNaN(newEnd) || newEnd === null) { + throw new Error('Invalid end "' + end + '"'); + } + + // prevent start < end + if (newEnd < newStart) { + newEnd = newStart; + } + + // prevent start < min + if (min !== null) { + if (newStart < min) { + diff = min - newStart; + newStart += diff; + newEnd += diff; + + // prevent end > max + if (max != null) { + if (newEnd > max) { + newEnd = max; + } + } + } + } + + // prevent end > max + if (max !== null) { + if (newEnd > max) { + diff = newEnd - max; + newStart -= diff; + newEnd -= diff; + + // prevent start < min + if (min != null) { + if (newStart < min) { + newStart = min; + } + } + } + } + + // prevent (end-start) < zoomMin + if (this.options.zoomMin !== null) { + var zoomMin = parseFloat(this.options.zoomMin); + if (zoomMin < 0) { + zoomMin = 0; + } + if (newEnd - newStart < zoomMin) { + if (this.end - this.start === zoomMin && newStart > this.start && newEnd < this.end) { + // ignore this action, we are already zoomed to the minimum + newStart = this.start; + newEnd = this.end; + } else { + // zoom to the minimum + diff = zoomMin - (newEnd - newStart); + newStart -= diff / 2; + newEnd += diff / 2; + } + } + } + + // prevent (end-start) > zoomMax + if (this.options.zoomMax !== null) { + var zoomMax = parseFloat(this.options.zoomMax); + if (zoomMax < 0) { + zoomMax = 0; + } + + if (newEnd - newStart > zoomMax) { + if (this.end - this.start === zoomMax && newStart < this.start && newEnd > this.end) { + // ignore this action, we are already zoomed to the maximum + newStart = this.start; + newEnd = this.end; + } else { + // zoom to the maximum + diff = newEnd - newStart - zoomMax; + newStart += diff / 2; + newEnd -= diff / 2; + } + } + } + + var changed = this.start != newStart || this.end != newEnd; + + // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not necessarily of type Range) + if (!(newStart >= this.start && newStart <= this.end || newEnd >= this.start && newEnd <= this.end) && !(this.start >= newStart && this.start <= newEnd || this.end >= newStart && this.end <= newEnd)) { + this.body.emitter.emit('checkRangedItems'); + } + + this.start = newStart; + this.end = newEnd; + return changed; + }; + + /** + * Retrieve the current range. + * @return {Object} An object with start and end properties + */ + Range.prototype.getRange = function () { + return { + start: this.start, + end: this.end + }; + }; + + /** + * Calculate the conversion offset and scale for current range, based on + * the provided width + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion + */ + Range.prototype.conversion = function (width, totalHidden) { + return Range.conversion(this.start, this.end, width, totalHidden); + }; + + /** + * Static method to calculate the conversion offset and scale for a range, + * based on the provided start, end, and width + * @param {Number} start + * @param {Number} end + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion + */ + Range.conversion = function (start, end, width, totalHidden) { + if (totalHidden === undefined) { + totalHidden = 0; + } + if (width != 0 && end - start != 0) { + return { + offset: start, + scale: width / (end - start - totalHidden) + }; + } else { + return { + offset: 0, + scale: 1 + }; + } + }; + + /** + * Start dragging horizontally or vertically + * @param {Event} event + * @private + */ + Range.prototype._onDragStart = function (event) { + this.deltaDifference = 0; + this.previousDelta = 0; + + // only allow dragging when configured as movable + if (!this.options.moveable) return; + + // only start dragging when the mouse is inside the current range + if (!this._isInsideRange(event)) return; + + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; + + this.props.touch.start = this.start; + this.props.touch.end = this.end; + this.props.touch.dragging = true; + + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'move'; + } + }; + + /** + * Perform dragging operation + * @param {Event} event + * @private + */ + Range.prototype._onDrag = function (event) { + if (!this.props.touch.dragging) return; + + // only allow dragging when configured as movable + if (!this.options.moveable) return; + + // TODO: this may be redundant in hammerjs2 + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; + + var direction = this.options.direction; + validateDirection(direction); + var delta = direction == 'horizontal' ? event.deltaX : event.deltaY; + delta -= this.deltaDifference; + var interval = this.props.touch.end - this.props.touch.start; + + // normalize dragging speed if cutout is in between. + var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + interval -= duration; + + var width = direction == 'horizontal' ? this.body.domProps.center.width : this.body.domProps.center.height; + var diffRange = -delta / width * interval; + var newStart = this.props.touch.start + diffRange; + var newEnd = this.props.touch.end + diffRange; + + // snapping times away from hidden zones var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta - delta, true); var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta - delta, true); if (safeStart != newStart || safeEnd != newEnd) { @@ -14087,9 +14086,7 @@ return /******/ (function(modules) { // webpackBootstrap /** * Destroy the component. Cleanup DOM and event listeners */ - Component.prototype.destroy = function () { - // should be implemented by the component - }; + Component.prototype.destroy = function () {}; /** * Test whether the component is resized since the last time _isResized() was @@ -14108,6 +14105,8 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Component; + // should be implemented by the component + /***/ }, /* 27 */ /***/ function(module, exports) { @@ -14190,16 +14189,16 @@ return /******/ (function(modules) { // webpackBootstrap } startDate.dayOfYear(start.dayOfYear()); startDate.year(start.year()); - startDate.subtract(7, 'days'); + startDate.subtract(7, "days"); endDate.dayOfYear(start.dayOfYear()); endDate.year(start.year()); - endDate.subtract(7 - offset, 'days'); + endDate.subtract(7 - offset, "days"); - runUntil.add(1, 'weeks'); + runUntil.add(1, "weeks"); break; case "weekly": - var dayOffset = endDate.diff(startDate, 'days'); + var dayOffset = endDate.diff(startDate, "days"); var day = startDate.day(); // set the start date to the range.start @@ -14211,12 +14210,12 @@ return /******/ (function(modules) { // webpackBootstrap // force startDate.day(day); endDate.day(day); - endDate.add(dayOffset, 'days'); + endDate.add(dayOffset, "days"); - startDate.subtract(1, 'weeks'); - endDate.subtract(1, 'weeks'); + startDate.subtract(1, "weeks"); + endDate.subtract(1, "weeks"); - runUntil.add(1, 'weeks'); + runUntil.add(1, "weeks"); break; case "monthly": if (startDate.month() != endDate.month()) { @@ -14224,26 +14223,26 @@ return /******/ (function(modules) { // webpackBootstrap } startDate.month(start.month()); startDate.year(start.year()); - startDate.subtract(1, 'months'); + startDate.subtract(1, "months"); endDate.month(start.month()); endDate.year(start.year()); - endDate.subtract(1, 'months'); - endDate.add(offset, 'months'); + endDate.subtract(1, "months"); + endDate.add(offset, "months"); - runUntil.add(1, 'months'); + runUntil.add(1, "months"); break; case "yearly": if (startDate.year() != endDate.year()) { offset = 1; } startDate.year(start.year()); - startDate.subtract(1, 'years'); + startDate.subtract(1, "years"); endDate.year(start.year()); - endDate.subtract(1, 'years'); - endDate.add(offset, 'years'); + endDate.subtract(1, "years"); + endDate.add(offset, "years"); - runUntil.add(1, 'years'); + runUntil.add(1, "years"); break; default: console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); @@ -14253,20 +14252,20 @@ return /******/ (function(modules) { // webpackBootstrap body.hiddenDates.push({ start: startDate.valueOf(), end: endDate.valueOf() }); switch (hiddenDates[i].repeat) { case "daily": - startDate.add(1, 'days'); - endDate.add(1, 'days'); + startDate.add(1, "days"); + endDate.add(1, "days"); break; case "weekly": - startDate.add(1, 'weeks'); - endDate.add(1, 'weeks'); + startDate.add(1, "weeks"); + endDate.add(1, "weeks"); break; case "monthly": - startDate.add(1, 'months'); - endDate.add(1, 'months'); + startDate.add(1, "months"); + endDate.add(1, "months"); break; case "yearly": - startDate.add(1, 'y'); - endDate.add(1, 'y'); + startDate.add(1, "y"); + endDate.add(1, "y"); break; default: console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); @@ -14313,14 +14312,14 @@ return /******/ (function(modules) { // webpackBootstrap } // j start inside i else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) { - hiddenDates[i].end = hiddenDates[j].end; - hiddenDates[j].remove = true; - } - // j end inside i - else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { - hiddenDates[i].start = hiddenDates[j].start; - hiddenDates[j].remove = true; - } + hiddenDates[i].end = hiddenDates[j].end; + hiddenDates[j].remove = true; + } + // j end inside i + else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { + hiddenDates[i].start = hiddenDates[j].start; + hiddenDates[j].remove = true; + } } } } @@ -15536,7 +15535,7 @@ return /******/ (function(modules) { // webpackBootstrap if (newScrollTop != oldScrollTop) { this._redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already - this.emit("verticalDrag"); + this.emit('verticalDrag'); } }; @@ -17122,25 +17121,25 @@ return /******/ (function(modules) { // webpackBootstrap } // if dragged group was move downwards everything above should have an offset else if (origOrder[curPos + orgOffset] == draggedId) { - orgOffset = 1; - continue; - } - // found a group (apart from dragged group) that has the wrong position -> switch with the - // group at the position where other one should be, fix index arrays and continue - else { - var slippedPosition = newOrder.indexOf(origOrder[curPos + orgOffset]); - var switchGroup = groupsData.get(newOrder[curPos + newOffset]); - var shouldBeGroup = groupsData.get(origOrder[curPos + orgOffset]); - this.options.groupOrderSwap(switchGroup, shouldBeGroup, groupsData); - groupsData.update(switchGroup); - groupsData.update(shouldBeGroup); - - var switchGroupId = newOrder[curPos + newOffset]; - newOrder[curPos + newOffset] = origOrder[curPos + orgOffset]; - newOrder[slippedPosition] = switchGroupId; - - curPos++; - } + orgOffset = 1; + continue; + } + // found a group (apart from dragged group) that has the wrong position -> switch with the + // group at the position where other one should be, fix index arrays and continue + else { + var slippedPosition = newOrder.indexOf(origOrder[curPos + orgOffset]); + var switchGroup = groupsData.get(newOrder[curPos + newOffset]); + var shouldBeGroup = groupsData.get(origOrder[curPos + orgOffset]); + this.options.groupOrderSwap(switchGroup, shouldBeGroup, groupsData); + groupsData.update(switchGroup); + groupsData.update(shouldBeGroup); + + var switchGroupId = newOrder[curPos + newOffset]; + newOrder[curPos + newOffset] = origOrder[curPos + orgOffset]; + newOrder[slippedPosition] = switchGroupId; + + curPos++; + } } } } @@ -17341,16 +17340,16 @@ return /******/ (function(modules) { // webpackBootstrap } } } else { - // add/remove this item from the current selection - var index = selection.indexOf(item.id); - if (index == -1) { - // item is not yet selected -> select it - selection.push(item.id); - } else { - // item is already selected -> deselect it - selection.splice(index, 1); - } + // add/remove this item from the current selection + var index = selection.indexOf(item.id); + if (index == -1) { + // item is not yet selected -> select it + selection.push(item.id); + } else { + // item is already selected -> deselect it + selection.splice(index, 1); } + } this.setSelection(selection); @@ -17616,7 +17615,7 @@ return /******/ (function(modules) { // webpackBootstrap */ TimeStep.prototype.setRange = function (start, end, minimumStep) { if (!(start instanceof Date) || !(end instanceof Date)) { - throw "No legal start or end date in method setRange"; + throw 'No legal start or end date in method setRange'; } this._start = start != undefined ? this.moment(start.valueOf()) : new Date(); @@ -17658,7 +17657,7 @@ return /******/ (function(modules) { // webpackBootstrap this.current.seconds(0); case 'second': this.current.milliseconds(0); - //case 'millisecond': // nothing to do for milliseconds + //case 'millisecond': // nothing to do for milliseconds } if (this.step != 1) { @@ -17951,8 +17950,8 @@ return /******/ (function(modules) { // webpackBootstrap clone.add(1, 'month'); // important: first set Date to 1, after that change the month. } else { - clone.date(1); - } + clone.date(1); + } clone.hours(0); clone.minutes(0); @@ -18239,7 +18238,7 @@ return /******/ (function(modules) { // webpackBootstrap }; this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap. var me = this; - this.itemSet.body.emitter.on("checkRangedItems", function () { + this.itemSet.body.emitter.on('checkRangedItems', function () { me.checkRangedItems = true; }); @@ -19128,10 +19127,10 @@ return /******/ (function(modules) { // webpackBootstrap // a width which will not change when moving the Timeline // So no re-stacking needed, which is nicer for the eye; } else { - this.left = start; - this.width = boxWidth; - contentWidth = Math.min(end - start, this.props.content.width); - } + this.left = start; + this.width = boxWidth; + contentWidth = Math.min(end - start, this.props.content.width); + } this.dom.box.style.left = this.left + 'px'; this.dom.box.style.width = boxWidth + 'px'; @@ -19159,12 +19158,12 @@ return /******/ (function(modules) { // webpackBootstrap contentLeft = -contentWidth; // ensure it's not visible anymore } } else { - if (start < 0) { - contentLeft = -start; - } else { - contentLeft = 0; - } + if (start < 0) { + contentLeft = -start; + } else { + contentLeft = 0; } + } this.dom.content.style.left = contentLeft + 'px'; } }; @@ -19356,23 +19355,17 @@ return /******/ (function(modules) { // webpackBootstrap /** * Repaint the item */ - Item.prototype.redraw = function () { - // should be implemented by the item - }; + Item.prototype.redraw = function () {}; /** * Reposition the Item horizontally */ - Item.prototype.repositionX = function () { - // should be implemented by the item - }; + Item.prototype.repositionX = function () {}; /** * Reposition the Item vertically */ - Item.prototype.repositionY = function () { - // should be implemented by the item - }; + Item.prototype.repositionY = function () {}; /** * Repaint a delete button on the top right of the item when the item is selected @@ -19531,6 +19524,12 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Item; + // should be implemented by the item + + // should be implemented by the item + + // should be implemented by the item + /***/ }, /* 35 */ /***/ function(module, exports, __webpack_require__) { @@ -20224,39 +20223,39 @@ return /******/ (function(modules) { // webpackBootstrap } // and when the orientation is bottom: else { - var newTop = this.parent.top; - var totalHeight = 0; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true) { - var newHeight = subgroups[subgroup].height + margin.item.vertical; - totalHeight += newHeight; - if (subgroups[subgroup].index > subgroupIndex) { - newTop += newHeight; - } + var newTop = this.parent.top; + var totalHeight = 0; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true) { + var newHeight = subgroups[subgroup].height + margin.item.vertical; + totalHeight += newHeight; + if (subgroups[subgroup].index > subgroupIndex) { + newTop += newHeight; } } } - height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; - this.dom.box.style.top = this.parent.height - totalHeight + newTop + 'px'; - this.dom.box.style.bottom = ''; } + height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; + this.dom.box.style.top = this.parent.height - totalHeight + newTop + 'px'; + this.dom.box.style.bottom = ''; + } } // and in the case of no subgroups: else { - // we want backgrounds with groups to only show in groups. - if (this.parent instanceof BackgroundGroup) { - // if the item is not in a group: - height = Math.max(this.parent.height, this.parent.itemSet.body.domProps.center.height, this.parent.itemSet.body.domProps.centerContainer.height); - this.dom.box.style.top = onTop ? '0' : ''; - this.dom.box.style.bottom = onTop ? '' : '0'; - } else { - height = this.parent.height; - // same alignment for items when orientation is top or bottom - this.dom.box.style.top = this.parent.top + 'px'; - this.dom.box.style.bottom = ''; - } + // we want backgrounds with groups to only show in groups. + if (this.parent instanceof BackgroundGroup) { + // if the item is not in a group: + height = Math.max(this.parent.height, this.parent.itemSet.body.domProps.center.height, this.parent.itemSet.body.domProps.centerContainer.height); + this.dom.box.style.top = onTop ? '0' : ''; + this.dom.box.style.bottom = onTop ? '' : '0'; + } else { + height = this.parent.height; + // same alignment for items when orientation is top or bottom + this.dom.box.style.top = this.parent.top + 'px'; + this.dom.box.style.bottom = ''; } + } this.dom.box.style.height = height + 'px'; }; @@ -20887,199 +20886,199 @@ return /******/ (function(modules) { // webpackBootstrap /* 41 */ /***/ function(module, exports, __webpack_require__) { - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; - /** - * Created by Alex on 11/6/2014. - */ - - // https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60 - // if the module has no dependencies, the above pattern can be simplified to - (function (root, factory) { - if (true) { - // AMD. Register as an anonymous module. - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } 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.keycharm = factory(); - } - }(this, function () { - - function keycharm(options) { - var preventDefault = options && options.preventDefault || false; - - var container = options && options.container || window; - - var _exportFunctions = {}; - var _bound = {keydown:{}, keyup:{}}; - var _keys = {}; - var i; - - // a - z - for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} - // A - Z - for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} - // 0 - 9 - for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} - // F1 - F12 - for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} - // num0 - num9 - for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} - - // numpad misc - _keys['num*'] = {code:106, shift: false}; - _keys['num+'] = {code:107, shift: false}; - _keys['num-'] = {code:109, shift: false}; - _keys['num/'] = {code:111, shift: false}; - _keys['num.'] = {code:110, shift: false}; - // arrows - _keys['left'] = {code:37, shift: false}; - _keys['up'] = {code:38, shift: false}; - _keys['right'] = {code:39, shift: false}; - _keys['down'] = {code:40, shift: false}; - // extra keys - _keys['space'] = {code:32, shift: false}; - _keys['enter'] = {code:13, shift: false}; - _keys['shift'] = {code:16, shift: undefined}; - _keys['esc'] = {code:27, shift: false}; - _keys['backspace'] = {code:8, shift: false}; - _keys['tab'] = {code:9, shift: false}; - _keys['ctrl'] = {code:17, shift: false}; - _keys['alt'] = {code:18, shift: false}; - _keys['delete'] = {code:46, shift: false}; - _keys['pageup'] = {code:33, shift: false}; - _keys['pagedown'] = {code:34, shift: false}; - // symbols - _keys['='] = {code:187, shift: false}; - _keys['-'] = {code:189, shift: false}; - _keys[']'] = {code:221, shift: false}; - _keys['['] = {code:219, shift: false}; - - - - var down = function(event) {handleEvent(event,'keydown');}; - var up = function(event) {handleEvent(event,'keyup');}; - - // handle the actualy bound key with the event - var handleEvent = function(event,type) { - if (_bound[type][event.keyCode] !== undefined) { - var bound = _bound[type][event.keyCode]; - for (var i = 0; i < bound.length; i++) { - if (bound[i].shift === undefined) { - bound[i].fn(event); - } - else if (bound[i].shift == true && event.shiftKey == true) { - bound[i].fn(event); - } - else if (bound[i].shift == false && event.shiftKey == false) { - bound[i].fn(event); - } - } - - if (preventDefault == true) { - event.preventDefault(); - } - } - }; - - // bind a key to a callback - _exportFunctions.bind = function(key, callback, type) { - if (type === undefined) { - type = 'keydown'; - } - if (_keys[key] === undefined) { - throw new Error("unsupported key: " + key); - } - if (_bound[type][_keys[key].code] === undefined) { - _bound[type][_keys[key].code] = []; - } - _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); - }; - - - // bind all keys to a call back (demo purposes) - _exportFunctions.bindAll = function(callback, type) { - if (type === undefined) { - type = 'keydown'; - } - for (var key in _keys) { - if (_keys.hasOwnProperty(key)) { - _exportFunctions.bind(key,callback,type); - } - } - }; - - // get the key label from an event - _exportFunctions.getKey = function(event) { - for (var key in _keys) { - if (_keys.hasOwnProperty(key)) { - if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { - return key; - } - else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { - return key; - } - else if (event.keyCode == _keys[key].code && key == 'shift') { - return key; - } - } - } - return "unknown key, currently not supported"; - }; - - // unbind either a specific callback from a key or all of them (by leaving callback undefined) - _exportFunctions.unbind = function(key, callback, type) { - if (type === undefined) { - type = 'keydown'; - } - if (_keys[key] === undefined) { - throw new Error("unsupported key: " + key); - } - if (callback !== undefined) { - var newBindings = []; - var bound = _bound[type][_keys[key].code]; - if (bound !== undefined) { - for (var i = 0; i < bound.length; i++) { - if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { - newBindings.push(_bound[type][_keys[key].code][i]); - } - } - } - _bound[type][_keys[key].code] = newBindings; - } - else { - _bound[type][_keys[key].code] = []; - } - }; - - // reset all bound variables. - _exportFunctions.reset = function() { - _bound = {keydown:{}, keyup:{}}; - }; - - // unbind all listeners and reset all variables. - _exportFunctions.destroy = function() { - _bound = {keydown:{}, keyup:{}}; - container.removeEventListener('keydown', down, true); - container.removeEventListener('keyup', up, true); - }; - - // create listeners. - container.addEventListener('keydown',down,true); - container.addEventListener('keyup',up,true); - - // return the public functions. - return _exportFunctions; - } - - return keycharm; - })); - - + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; + /** + * Created by Alex on 11/6/2014. + */ + + // https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60 + // if the module has no dependencies, the above pattern can be simplified to + (function (root, factory) { + if (true) { + // AMD. Register as an anonymous module. + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } 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.keycharm = factory(); + } + }(this, function () { + + function keycharm(options) { + var preventDefault = options && options.preventDefault || false; + + var container = options && options.container || window; + + var _exportFunctions = {}; + var _bound = {keydown:{}, keyup:{}}; + var _keys = {}; + var i; + + // a - z + for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} + // A - Z + for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} + // 0 - 9 + for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} + // F1 - F12 + for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} + // num0 - num9 + for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} + + // numpad misc + _keys['num*'] = {code:106, shift: false}; + _keys['num+'] = {code:107, shift: false}; + _keys['num-'] = {code:109, shift: false}; + _keys['num/'] = {code:111, shift: false}; + _keys['num.'] = {code:110, shift: false}; + // arrows + _keys['left'] = {code:37, shift: false}; + _keys['up'] = {code:38, shift: false}; + _keys['right'] = {code:39, shift: false}; + _keys['down'] = {code:40, shift: false}; + // extra keys + _keys['space'] = {code:32, shift: false}; + _keys['enter'] = {code:13, shift: false}; + _keys['shift'] = {code:16, shift: undefined}; + _keys['esc'] = {code:27, shift: false}; + _keys['backspace'] = {code:8, shift: false}; + _keys['tab'] = {code:9, shift: false}; + _keys['ctrl'] = {code:17, shift: false}; + _keys['alt'] = {code:18, shift: false}; + _keys['delete'] = {code:46, shift: false}; + _keys['pageup'] = {code:33, shift: false}; + _keys['pagedown'] = {code:34, shift: false}; + // symbols + _keys['='] = {code:187, shift: false}; + _keys['-'] = {code:189, shift: false}; + _keys[']'] = {code:221, shift: false}; + _keys['['] = {code:219, shift: false}; + + + + var down = function(event) {handleEvent(event,'keydown');}; + var up = function(event) {handleEvent(event,'keyup');}; + + // handle the actualy bound key with the event + var handleEvent = function(event,type) { + if (_bound[type][event.keyCode] !== undefined) { + var bound = _bound[type][event.keyCode]; + for (var i = 0; i < bound.length; i++) { + if (bound[i].shift === undefined) { + bound[i].fn(event); + } + else if (bound[i].shift == true && event.shiftKey == true) { + bound[i].fn(event); + } + else if (bound[i].shift == false && event.shiftKey == false) { + bound[i].fn(event); + } + } + + if (preventDefault == true) { + event.preventDefault(); + } + } + }; + + // bind a key to a callback + _exportFunctions.bind = function(key, callback, type) { + if (type === undefined) { + type = 'keydown'; + } + if (_keys[key] === undefined) { + throw new Error("unsupported key: " + key); + } + if (_bound[type][_keys[key].code] === undefined) { + _bound[type][_keys[key].code] = []; + } + _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); + }; + + + // bind all keys to a call back (demo purposes) + _exportFunctions.bindAll = function(callback, type) { + if (type === undefined) { + type = 'keydown'; + } + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + _exportFunctions.bind(key,callback,type); + } + } + }; + + // get the key label from an event + _exportFunctions.getKey = function(event) { + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { + return key; + } + else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { + return key; + } + else if (event.keyCode == _keys[key].code && key == 'shift') { + return key; + } + } + } + return "unknown key, currently not supported"; + }; + + // unbind either a specific callback from a key or all of them (by leaving callback undefined) + _exportFunctions.unbind = function(key, callback, type) { + if (type === undefined) { + type = 'keydown'; + } + if (_keys[key] === undefined) { + throw new Error("unsupported key: " + key); + } + if (callback !== undefined) { + var newBindings = []; + var bound = _bound[type][_keys[key].code]; + if (bound !== undefined) { + for (var i = 0; i < bound.length; i++) { + if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { + newBindings.push(_bound[type][_keys[key].code][i]); + } + } + } + _bound[type][_keys[key].code] = newBindings; + } + else { + _bound[type][_keys[key].code] = []; + } + }; + + // reset all bound variables. + _exportFunctions.reset = function() { + _bound = {keydown:{}, keyup:{}}; + }; + + // unbind all listeners and reset all variables. + _exportFunctions.destroy = function() { + _bound = {keydown:{}, keyup:{}}; + container.removeEventListener('keydown', down, true); + container.removeEventListener('keyup', up, true); + }; + + // create listeners. + container.addEventListener('keydown',down,true); + container.addEventListener('keyup',up,true); + + // return the public functions. + return _exportFunctions; + } + + return keycharm; + })); + + /***/ }, @@ -21552,6 +21551,8 @@ return /******/ (function(modules) { // webpackBootstrap var _ColorPicker2 = _interopRequireDefault(_ColorPicker); + var util = __webpack_require__(1); + /** * The way this works is for all properties of this.possible options, you can supply the property name in any form to list the options. * Boolean options are recognised as Boolean @@ -21566,7 +21567,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param configureOptions | the fully configured and predefined options set found in allOptions.js * @param pixelRatio | canvas pixel ratio */ - var util = __webpack_require__(1); var Configurator = (function () { function Configurator(parentModule, defaultContainer, configureOptions) { @@ -21600,15 +21600,15 @@ return /******/ (function(modules) { // webpackBootstrap this.wrapper = undefined; } - /** - * refresh all options. - * Because all modules parse their options by themselves, we just use their options. We copy them here. - * - * @param options - */ - _createClass(Configurator, [{ key: 'setOptions', + + /** + * refresh all options. + * Because all modules parse their options by themselves, we just use their options. We copy them here. + * + * @param options + */ value: function setOptions(options) { if (options !== undefined) { // reset the popup history because the indices may have been changed. @@ -21660,13 +21660,13 @@ return /******/ (function(modules) { // webpackBootstrap this._create(); } } + }, { + key: '_create', /** * Create all DOM elements * @private */ - }, { - key: '_create', value: function _create() { var _this = this; @@ -21730,13 +21730,13 @@ return /******/ (function(modules) { // webpackBootstrap this._push(); this.colorPicker.insertTo(this.container); } + }, { + key: '_push', /** * draw all DOM elements on the screen * @private */ - }, { - key: '_push', value: function _push() { this.wrapper = document.createElement('div'); this.wrapper.className = 'vis-configuration-wrapper'; @@ -21747,13 +21747,13 @@ return /******/ (function(modules) { // webpackBootstrap this._showPopupIfNeeded(); } + }, { + key: '_clean', /** * delete all DOM elements * @private */ - }, { - key: '_clean', value: function _clean() { for (var i = 0; i < this.domElements.length; i++) { this.wrapper.removeChild(this.domElements[i]); @@ -21767,6 +21767,8 @@ return /******/ (function(modules) { // webpackBootstrap this._removePopup(); } + }, { + key: '_getValue', /** * get the value from the actualOptions if it exists @@ -21774,8 +21776,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ - }, { - key: '_getValue', value: function _getValue(path) { var base = this.moduleOptions; for (var i = 0; i < path.length; i++) { @@ -21788,6 +21788,8 @@ return /******/ (function(modules) { // webpackBootstrap } return base; } + }, { + key: '_makeItem', /** * all option elements are wrapped in an item @@ -21795,8 +21797,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param domElements * @private */ - }, { - key: '_makeItem', value: function _makeItem(path) { var _arguments = arguments, _this2 = this; @@ -21825,20 +21825,22 @@ return /******/ (function(modules) { // webpackBootstrap } return 0; } + }, { + key: '_makeHeader', /** * header for major subjects * @param name * @private */ - }, { - key: '_makeHeader', value: function _makeHeader(name) { var div = document.createElement('div'); div.className = 'vis-configuration vis-config-header'; div.innerHTML = name; this._makeItem([], div); } + }, { + key: '_makeLabel', /** * make a label, if it is an object label, it gets different styling. @@ -21848,8 +21850,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {HTMLElement} * @private */ - }, { - key: '_makeLabel', value: function _makeLabel(name, path) { var objectLabel = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; @@ -21862,6 +21862,8 @@ return /******/ (function(modules) { // webpackBootstrap } return div; } + }, { + key: '_makeDropdown', /** * make a dropdown list for multiple possible string optoins @@ -21870,8 +21872,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ - }, { - key: '_makeDropdown', value: function _makeDropdown(arr, value, path) { var select = document.createElement('select'); select.className = 'vis-configuration vis-config-select'; @@ -21900,6 +21900,8 @@ return /******/ (function(modules) { // webpackBootstrap var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, select); } + }, { + key: '_makeRange', /** * make a range object for numeric options @@ -21908,8 +21910,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ - }, { - key: '_makeRange', value: function _makeRange(arr, value, path) { var defaultValue = arr[0]; var min = arr[1]; @@ -21970,6 +21970,8 @@ return /******/ (function(modules) { // webpackBootstrap this._setupPopup(popupString, itemIndex); } } + }, { + key: '_setupPopup', /** * prepare the popup @@ -21977,15 +21979,13 @@ return /******/ (function(modules) { // webpackBootstrap * @param index * @private */ - }, { - key: '_setupPopup', value: function _setupPopup(string, index) { var _this3 = this; if (this.initialized === true && this.allowCreation === true && this.popupCounter < this.popupLimit) { - var div = document.createElement("div"); - div.id = "vis-configuration-popup"; - div.className = "vis-configuration-popup"; + var div = document.createElement('div'); + div.id = 'vis-configuration-popup'; + div.className = 'vis-configuration-popup'; div.innerHTML = string; div.onclick = function () { _this3._removePopup(); @@ -21994,13 +21994,13 @@ return /******/ (function(modules) { // webpackBootstrap this.popupDiv = { html: div, index: index }; } } + }, { + key: '_removePopup', /** * remove the popup from the dom * @private */ - }, { - key: '_removePopup', value: function _removePopup() { if (this.popupDiv.html !== undefined) { this.popupDiv.html.parentNode.removeChild(this.popupDiv.html); @@ -22009,21 +22009,21 @@ return /******/ (function(modules) { // webpackBootstrap this.popupDiv = {}; } } + }, { + key: '_showPopupIfNeeded', /** * Show the popup if it is needed. * @private */ - }, { - key: '_showPopupIfNeeded', value: function _showPopupIfNeeded() { var _this4 = this; if (this.popupDiv.html !== undefined) { var correspondingElement = this.domElements[this.popupDiv.index]; var rect = correspondingElement.getBoundingClientRect(); - this.popupDiv.html.style.left = rect.left + "px"; - this.popupDiv.html.style.top = rect.top - 30 + "px"; // 30 is the height; + this.popupDiv.html.style.left = rect.left + 'px'; + this.popupDiv.html.style.top = rect.top - 30 + 'px'; // 30 is the height; document.body.appendChild(this.popupDiv.html); this.popupDiv.hideTimeout = setTimeout(function () { _this4.popupDiv.html.style.opacity = 0; @@ -22033,6 +22033,8 @@ return /******/ (function(modules) { // webpackBootstrap }, 1800); } } + }, { + key: '_makeCheckbox', /** * make a checkbox for boolean options. @@ -22041,8 +22043,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ - }, { - key: '_makeCheckbox', value: function _makeCheckbox(defaultValue, value, path) { var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; @@ -22069,6 +22069,8 @@ return /******/ (function(modules) { // webpackBootstrap var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, checkbox); } + }, { + key: '_makeTextInput', /** * make a text input field for string options. @@ -22077,8 +22079,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ - }, { - key: '_makeTextInput', value: function _makeTextInput(defaultValue, value, path) { var checkbox = document.createElement('input'); checkbox.type = 'text'; @@ -22096,6 +22096,8 @@ return /******/ (function(modules) { // webpackBootstrap var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, checkbox); } + }, { + key: '_makeColorField', /** * make a color field with a color picker for color fields @@ -22104,8 +22106,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ - }, { - key: '_makeColorField', value: function _makeColorField(arr, value, path) { var _this5 = this; @@ -22128,6 +22128,8 @@ return /******/ (function(modules) { // webpackBootstrap var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, div); } + }, { + key: '_showColorPicker', /** * used by the color buttons to call the color picker. @@ -22137,8 +22139,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ - }, { - key: '_showColorPicker', value: function _showColorPicker(value, div, path) { var _this6 = this; @@ -22154,6 +22154,8 @@ return /******/ (function(modules) { // webpackBootstrap _this6._update(colorString, path); }); } + }, { + key: '_handleObject', /** * parse an object and draw the correct items @@ -22161,8 +22163,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ - }, { - key: '_handleObject', value: function _handleObject(obj) { var path = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; var checkOnly = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; @@ -22233,6 +22233,8 @@ return /******/ (function(modules) { // webpackBootstrap } return visibleInSet; } + }, { + key: '_handleArray', /** * handle the array type of option @@ -22242,8 +22244,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ - }, { - key: '_handleArray', value: function _handleArray(arr, value, path) { if (typeof arr[0] === 'string' && arr[0] === 'color') { this._makeColorField(arr, value, path); @@ -22262,6 +22262,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_update', /** * called to update the network with the new settings. @@ -22269,13 +22271,11 @@ return /******/ (function(modules) { // webpackBootstrap * @param path * @private */ - }, { - key: '_update', value: function _update(value, path) { var options = this._constructOptions(value, path); if (this.parent.body && this.parent.body.emitter && this.parent.body.emitter.emit) { - this.parent.body.emitter.emit("configChange", options); + this.parent.body.emitter.emit('configChange', options); } this.initialized = true; this.parent.setOptions(options); @@ -22369,13 +22369,13 @@ return /******/ (function(modules) { // webpackBootstrap this._create(); } - /** - * this inserts the colorPicker into a div from the DOM - * @param container - */ - _createClass(ColorPicker, [{ key: 'insertTo', + + /** + * this inserts the colorPicker into a div from the DOM + * @param container + */ value: function insertTo(container) { if (this.hammer !== undefined) { this.hammer.destroy(); @@ -22387,18 +22387,18 @@ return /******/ (function(modules) { // webpackBootstrap this._setSize(); } + }, { + key: 'setCallback', /** * the callback is executed on apply and save. Bind it to the application * @param callback */ - }, { - key: 'setCallback', value: function setCallback(callback) { if (typeof callback === 'function') { this.updateCallback = callback; } else { - throw new Error("Function attempted to set as colorPicker callback is not a function."); + throw new Error('Function attempted to set as colorPicker callback is not a function.'); } } }, { @@ -22409,6 +22409,8 @@ return /******/ (function(modules) { // webpackBootstrap return htmlColors[color]; } } + }, { + key: 'setColor', /** * Set the color of the colorPicker @@ -22422,8 +22424,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param color * @param setInitial */ - }, { - key: 'setColor', value: function setColor(color) { var setInitial = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; @@ -22462,19 +22462,19 @@ return /******/ (function(modules) { // webpackBootstrap // set color if (rgba === undefined) { - throw new Error("Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: " + JSON.stringify(color)); + throw new Error('Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: ' + JSON.stringify(color)); } else { this._setColor(rgba, setInitial); } } + }, { + key: 'show', /** * this shows the color picker at a location. The hue circle is constructed once and stored. * @param x * @param y */ - }, { - key: 'show', value: function show(x, y) { this.applied = false; this.frame.style.display = 'block'; @@ -22482,6 +22482,8 @@ return /******/ (function(modules) { // webpackBootstrap this.frame.style.left = x + 'px'; this._generateHueCircle(); } + }, { + key: '_hide', // ------------------------------------------ PRIVATE ----------------------------- // @@ -22491,8 +22493,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param storePrevious * @private */ - }, { - key: '_hide', value: function _hide() { var storePrevious = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0]; @@ -22507,44 +22507,46 @@ return /******/ (function(modules) { // webpackBootstrap this.frame.style.display = 'none'; } + }, { + key: '_save', /** * bound to the save button. Saves and hides. * @private */ - }, { - key: '_save', value: function _save() { this.updateCallback(this.color); this.applied = false; this._hide(); } + }, { + key: '_apply', /** * Bound to apply button. Saves but does not close. Is undone by the cancel button. * @private */ - }, { - key: '_apply', value: function _apply() { this.applied = true; this.updateCallback(this.color); this._updatePicker(this.color); } + }, { + key: '_loadLast', /** * load the color from the previous session. * @private */ - }, { - key: '_loadLast', value: function _loadLast() { if (this.previousColor !== undefined) { this.setColor(this.previousColor, false); } else { - alert("There is no last color to load..."); + alert('There is no last color to load...'); } } + }, { + key: '_setColor', /** * set the color, place the picker @@ -22552,8 +22554,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param setInitial * @private */ - }, { - key: '_setColor', value: function _setColor(rgba) { var setInitial = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; @@ -22575,26 +22575,26 @@ return /******/ (function(modules) { // webpackBootstrap this._updatePicker(rgba); } + }, { + key: '_setOpacity', /** * bound to opacity control * @param value * @private */ - }, { - key: '_setOpacity', value: function _setOpacity(value) { this.color.a = value / 100; this._updatePicker(this.color); } + }, { + key: '_setBrightness', /** * bound to brightness control * @param value * @private */ - }, { - key: '_setBrightness', value: function _setBrightness(value) { var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); hsv.v = value / 100; @@ -22603,14 +22603,14 @@ return /******/ (function(modules) { // webpackBootstrap this.color = rgba; this._updatePicker(); } + }, { + key: '_updatePicker', /** * update the colorpicker. A black circle overlays the hue circle to mimic the brightness decreasing. * @param rgba * @private */ - }, { - key: '_updatePicker', value: function _updatePicker() { var rgba = arguments.length <= 0 || arguments[0] === undefined ? this.color : arguments[0]; @@ -22637,13 +22637,13 @@ return /******/ (function(modules) { // webpackBootstrap this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; } + }, { + key: '_setSize', /** * used by create to set the size of the canvas. * @private */ - }, { - key: '_setSize', value: function _setSize() { this.colorPickerCanvas.style.width = '100%'; this.colorPickerCanvas.style.height = '100%'; @@ -22651,14 +22651,14 @@ return /******/ (function(modules) { // webpackBootstrap this.colorPickerCanvas.width = 289 * this.pixelRatio; this.colorPickerCanvas.height = 289 * this.pixelRatio; } + }, { + key: '_create', /** * create all dom elements * TODO: cleanup, lots of similar dom elements * @private */ - }, { - key: '_create', value: function _create() { this.frame = document.createElement('div'); this.frame.className = 'vis-color-picker'; @@ -22679,10 +22679,10 @@ return /******/ (function(modules) { // webpackBootstrap noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; this.colorPickerCanvas.appendChild(noCanvas); } else { - var ctx = this.colorPickerCanvas.getContext("2d"); + var ctx = this.colorPickerCanvas.getContext('2d'); this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); - this.colorPickerCanvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + this.colorPickerCanvas.getContext('2d').setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); } this.colorPickerDiv.className = 'vis-color'; @@ -22731,39 +22731,39 @@ return /******/ (function(modules) { // webpackBootstrap me._setBrightness(this.value); }; - this.brightnessLabel = document.createElement("div"); - this.brightnessLabel.className = "vis-label vis-brightness"; + this.brightnessLabel = document.createElement('div'); + this.brightnessLabel.className = 'vis-label vis-brightness'; this.brightnessLabel.innerHTML = 'brightness:'; - this.opacityLabel = document.createElement("div"); - this.opacityLabel.className = "vis-label vis-opacity"; + this.opacityLabel = document.createElement('div'); + this.opacityLabel.className = 'vis-label vis-opacity'; this.opacityLabel.innerHTML = 'opacity:'; - this.newColorDiv = document.createElement("div"); - this.newColorDiv.className = "vis-new-color"; + this.newColorDiv = document.createElement('div'); + this.newColorDiv.className = 'vis-new-color'; this.newColorDiv.innerHTML = 'new'; - this.initialColorDiv = document.createElement("div"); - this.initialColorDiv.className = "vis-initial-color"; + this.initialColorDiv = document.createElement('div'); + this.initialColorDiv.className = 'vis-initial-color'; this.initialColorDiv.innerHTML = 'initial'; - this.cancelButton = document.createElement("div"); - this.cancelButton.className = "vis-button vis-cancel"; + this.cancelButton = document.createElement('div'); + this.cancelButton.className = 'vis-button vis-cancel'; this.cancelButton.innerHTML = 'cancel'; this.cancelButton.onclick = this._hide.bind(this, false); - this.applyButton = document.createElement("div"); - this.applyButton.className = "vis-button vis-apply"; + this.applyButton = document.createElement('div'); + this.applyButton.className = 'vis-button vis-apply'; this.applyButton.innerHTML = 'apply'; this.applyButton.onclick = this._apply.bind(this); - this.saveButton = document.createElement("div"); - this.saveButton.className = "vis-button vis-save"; + this.saveButton = document.createElement('div'); + this.saveButton.className = 'vis-button vis-save'; this.saveButton.innerHTML = 'save'; this.saveButton.onclick = this._save.bind(this); - this.loadButton = document.createElement("div"); - this.loadButton.className = "vis-button vis-load"; + this.loadButton = document.createElement('div'); + this.loadButton.className = 'vis-button vis-load'; this.loadButton.innerHTML = 'load last'; this.loadButton.onclick = this._loadLast.bind(this); @@ -22781,13 +22781,13 @@ return /******/ (function(modules) { // webpackBootstrap this.frame.appendChild(this.saveButton); this.frame.appendChild(this.loadButton); } + }, { + key: '_bindHammer', /** * bind hammer to the color picker * @private */ - }, { - key: '_bindHammer', value: function _bindHammer() { var _this = this; @@ -22812,13 +22812,13 @@ return /******/ (function(modules) { // webpackBootstrap _this._moveSelector(event); }); } + }, { + key: '_generateHueCircle', /** * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown. * @private */ - }, { - key: '_generateHueCircle', value: function _generateHueCircle() { if (this.generated === false) { var ctx = this.colorPickerCanvas.getContext('2d'); @@ -22860,6 +22860,8 @@ return /******/ (function(modules) { // webpackBootstrap } this.generated = true; } + }, { + key: '_moveSelector', /** * move the selector. This is called by hammer functions. @@ -22867,8 +22869,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param event * @private */ - }, { - key: '_moveSelector', value: function _moveSelector(event) { var rect = this.colorPickerDiv.getBoundingClientRect(); var left = event.center.x - rect.left; @@ -22940,15 +22940,15 @@ return /******/ (function(modules) { // webpackBootstrap _classCallCheck(this, Validator); } - /** - * Main function to be called - * @param options - * @param subObject - * @returns {boolean} - */ - _createClass(Validator, null, [{ key: 'validate', + + /** + * Main function to be called + * @param options + * @param subObject + * @returns {boolean} + */ value: function validate(options, referenceOptions, subObject) { errorFound = false; allOptions = referenceOptions; @@ -22959,6 +22959,8 @@ return /******/ (function(modules) { // webpackBootstrap Validator.parse(options, usedOptions, []); return errorFound; } + }, { + key: 'parse', /** * Will traverse an object recursively and check every value @@ -22966,8 +22968,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param referenceOptions * @param path */ - }, { - key: 'parse', value: function parse(options, referenceOptions, path) { for (var option in options) { if (options.hasOwnProperty(option)) { @@ -22975,6 +22975,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'check', /** * Check every value. If the value is an object, call the parse function on that object. @@ -22983,8 +22985,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param referenceOptions * @param path */ - }, { - key: 'check', value: function check(option, options, referenceOptions, path) { if (referenceOptions[option] === undefined && referenceOptions.__any__ === undefined) { Validator.getSuggestion(option, referenceOptions, path); @@ -23006,6 +23006,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'checkFields', /** * @@ -23016,8 +23018,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {String} refOptionType | This is the type object from the reference options * @param {Array} path | where in the object is the option */ - }, { - key: 'checkFields', value: function checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path) { var optionType = Validator.getType(options[option]); var refOptionType = refOptionObj[optionType]; @@ -23027,11 +23027,11 @@ return /******/ (function(modules) { // webpackBootstrap if (refOptionType.indexOf(options[option]) === -1) { console.log('%cInvalid option detected in "' + option + '".' + ' Allowed values are:' + Validator.print(refOptionType) + ' not "' + options[option] + '". ' + Validator.printLocation(path, option), printStyle); errorFound = true; - } else if (optionType === 'object' && referenceOption !== "__any__") { + } else if (optionType === 'object' && referenceOption !== '__any__') { path = util.copyAndExtendArray(path, option); Validator.parse(options[option], referenceOptions[referenceOption], path); } - } else if (optionType === 'object' && referenceOption !== "__any__") { + } else if (optionType === 'object' && referenceOption !== '__any__') { path = util.copyAndExtendArray(path, option); Validator.parse(options[option], referenceOptions[referenceOption], path); } @@ -23104,6 +23104,8 @@ return /******/ (function(modules) { // webpackBootstrap errorFound = true; } + }, { + key: 'findInOptions', /** * traverse the options in search for a match. @@ -23113,8 +23115,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param recursive * @returns {{closestMatch: string, path: Array, distance: number}} */ - }, { - key: 'findInOptions', value: function findInOptions(option, options, path) { var recursive = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3]; @@ -23174,8 +23174,10 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: 'print', value: function print(options) { - return JSON.stringify(options).replace(/(\")|(\[)|(\])|(,"__type__")/g, "").replace(/(\,)/g, ', '); + return JSON.stringify(options).replace(/(\")|(\[)|(\])|(,"__type__")/g, '').replace(/(\,)/g, ', '); } + }, { + key: 'levenshteinDistance', // Compute the edit distance between the two given strings // http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript @@ -23185,8 +23187,6 @@ return /******/ (function(modules) { // webpackBootstrap 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. */ - }, { - key: 'levenshteinDistance', value: function levenshteinDistance(a, b) { if (a.length === 0) return b.length; if (b.length === 0) return a.length; @@ -23685,7 +23685,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this.linegraph.groups[groupId] !== undefined) { return this.linegraph.groups[groupId].getLegend(width, height); } else { - return "cannot find group:" + groupId; + return 'cannot find group:' + groupId; } }; @@ -24002,7 +24002,7 @@ return /******/ (function(modules) { // webpackBootstrap this.updateSVGheight = true; this.updateSVGheightOnResize = true; } else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { - if (parseInt((options.graphHeight + '').replace("px", '')) < this.body.domProps.centerContainer.height) { + if (parseInt((options.graphHeight + '').replace('px', '')) < this.body.domProps.centerContainer.height) { this.updateSVGheight = true; } } @@ -24356,7 +24356,7 @@ return /******/ (function(modules) { // webpackBootstrap this.svg.style.left = util.option.asSize(-this.props.width); // if the height of the graph is set as proportional, change the height of the svg - if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) { + if ((this.options.height + '').indexOf('%') != -1 || this.updateSVGheightOnResize == true) { this.updateSVGheight = true; } } @@ -24448,7 +24448,7 @@ return /******/ (function(modules) { // webpackBootstrap return true; } else { if (this.COUNTER > MAX_CYCLES) { - console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle."); + console.log('WARNING: there may be an infinite loop in the _updateGraph emitter cycle.'); } this.COUNTER = 0; this.abortedGraphUpdate = false; @@ -24847,7 +24847,7 @@ return /******/ (function(modules) { // webpackBootstrap this.conversionFactor = 1; this.setOptions(options); - this.width = Number(('' + this.options.width).replace("px", "")); + this.width = Number(('' + this.options.width).replace('px', '')); this.minWidth = this.width; this.height = this.linegraphSVG.offsetHeight; this.hidden = false; @@ -24868,7 +24868,7 @@ return /******/ (function(modules) { // webpackBootstrap this._create(); var me = this; - this.body.emitter.on("verticalDrag", function () { + this.body.emitter.on('verticalDrag', function () { me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; }); } @@ -24902,7 +24902,7 @@ return /******/ (function(modules) { // webpackBootstrap var fields = ['orientation', 'showMinorLabels', 'showMajorLabels', 'icons', 'majorLinesOffset', 'minorLinesOffset', 'labelOffsetX', 'labelOffsetY', 'iconWidth', 'width', 'visible', 'left', 'right', 'alignZeros']; util.selectiveExtend(fields, this.options, options); - this.minWidth = Number(('' + this.options.width).replace("px", "")); + this.minWidth = Number(('' + this.options.width).replace('px', '')); if (redraw === true && this.dom.frame) { this.hide(); @@ -24925,12 +24925,12 @@ return /******/ (function(modules) { // webpackBootstrap this.dom.lineContainer.style.position = 'relative'; // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); - this.svg.style.position = "absolute"; + this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + this.svg.style.position = 'absolute'; this.svg.style.top = '0px'; this.svg.style.height = '100%'; this.svg.style.width = '100%'; - this.svg.style.display = "block"; + this.svg.style.display = 'block'; this.dom.frame.appendChild(this.svg); }; @@ -25044,11 +25044,11 @@ return /******/ (function(modules) { // webpackBootstrap this.hide(); } else { this.show(); - this.height = Number(this.linegraphSVG.style.height.replace("px", "")); + this.height = Number(this.linegraphSVG.style.height.replace('px', '')); // svg offsetheight did not work in firefox and explorer... this.dom.lineContainer.style.height = this.height + 'px'; - this.width = this.options.visible === true ? Number(('' + this.options.width).replace("px", "")) : 0; + this.width = this.options.visible === true ? Number(('' + this.options.width).replace('px', '')) : 0; var props = this.props; var frame = this.dom.frame; @@ -25078,7 +25078,7 @@ return /******/ (function(modules) { // webpackBootstrap frame.style.left = '0'; frame.style.bottom = ''; frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; + frame.style.height = this.height + 'px'; this.props.width = this.body.domProps.left.width; this.props.height = this.body.domProps.left.height; } else { @@ -25087,7 +25087,7 @@ return /******/ (function(modules) { // webpackBootstrap frame.style.bottom = '0'; frame.style.left = '0'; frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; + frame.style.height = this.height + 'px'; this.props.width = this.body.domProps.right.width; this.props.height = this.body.domProps.right.height; } @@ -25211,7 +25211,7 @@ return /******/ (function(modules) { // webpackBootstrap // this will resize the yAxis to accommodate the labels. if (this.maxLabelSize > this.width - offset && this.options.visible === true) { this.width = this.maxLabelSize + offset; - this.options.width = this.width + "px"; + this.options.width = this.width + 'px'; DOMutil.cleanupElements(this.DOMelements.lines); DOMutil.cleanupElements(this.DOMelements.labels); this.redraw(); @@ -25219,17 +25219,17 @@ return /******/ (function(modules) { // webpackBootstrap } // this will resize the yAxis if it is too big for the labels. else if (this.maxLabelSize < this.width - offset && this.options.visible === true && this.width > this.minWidth) { - this.width = Math.max(this.minWidth, this.maxLabelSize + offset); - this.options.width = this.width + "px"; - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - this.redraw(); - resized = true; - } else { - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - resized = false; - } + this.width = Math.max(this.minWidth, this.maxLabelSize + offset); + this.options.width = this.width + 'px'; + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + this.redraw(); + resized = true; + } else { + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + resized = false; + } return resized; }; @@ -25260,10 +25260,10 @@ return /******/ (function(modules) { // webpackBootstrap label.innerHTML = text; if (orientation === 'left') { label.style.left = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "right"; + label.style.textAlign = 'right'; } else { label.style.right = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "left"; + label.style.textAlign = 'left'; } label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; @@ -25747,29 +25747,29 @@ return /******/ (function(modules) { // webpackBootstrap var fillHeight = iconHeight * 0.5; var path, fillPath; - var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); - outline.setAttributeNS(null, "x", x); - outline.setAttributeNS(null, "y", y - fillHeight); - outline.setAttributeNS(null, "width", iconWidth); - outline.setAttributeNS(null, "height", 2 * fillHeight); - outline.setAttributeNS(null, "class", "vis-outline"); + var outline = DOMutil.getSVGElement('rect', JSONcontainer, SVGcontainer); + outline.setAttributeNS(null, 'x', x); + outline.setAttributeNS(null, 'y', y - fillHeight); + outline.setAttributeNS(null, 'width', iconWidth); + outline.setAttributeNS(null, 'height', 2 * fillHeight); + outline.setAttributeNS(null, 'class', 'vis-outline'); if (this.options.style == 'line') { - path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - path.setAttributeNS(null, "class", this.className); + path = DOMutil.getSVGElement('path', JSONcontainer, SVGcontainer); + path.setAttributeNS(null, 'class', this.className); if (this.style !== undefined) { - path.setAttributeNS(null, "style", this.style); + path.setAttributeNS(null, 'style', this.style); } - path.setAttributeNS(null, "d", "M" + x + "," + y + " L" + (x + iconWidth) + "," + y + ""); + path.setAttributeNS(null, 'd', 'M' + x + ',' + y + ' L' + (x + iconWidth) + ',' + y + ''); if (this.options.shaded.enabled == true) { - fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + fillPath = DOMutil.getSVGElement('path', JSONcontainer, SVGcontainer); if (this.options.shaded.orientation == 'top') { - fillPath.setAttributeNS(null, "d", "M" + x + ", " + (y - fillHeight) + "L" + x + "," + y + " L" + (x + iconWidth) + "," + y + " L" + (x + iconWidth) + "," + (y - fillHeight)); + fillPath.setAttributeNS(null, 'd', 'M' + x + ', ' + (y - fillHeight) + 'L' + x + ',' + y + ' L' + (x + iconWidth) + ',' + y + ' L' + (x + iconWidth) + ',' + (y - fillHeight)); } else { - fillPath.setAttributeNS(null, "d", "M" + x + "," + y + " " + "L" + x + "," + (y + fillHeight) + " " + "L" + (x + iconWidth) + "," + (y + fillHeight) + "L" + (x + iconWidth) + "," + y); + fillPath.setAttributeNS(null, 'd', 'M' + x + ',' + y + ' ' + 'L' + x + ',' + (y + fillHeight) + ' ' + 'L' + (x + iconWidth) + ',' + (y + fillHeight) + 'L' + (x + iconWidth) + ',' + y); } - fillPath.setAttributeNS(null, "class", this.className + " vis-icon-fill"); + fillPath.setAttributeNS(null, 'class', this.className + ' vis-icon-fill'); } if (this.options.drawPoints.enabled == true) { @@ -25801,7 +25801,7 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOrientation|*)}} */ GraphGroup.prototype.getLegend = function (iconWidth, iconHeight) { - var svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); + var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.drawIcon(0, 0.5 * iconHeight, [], svg, iconWidth, iconHeight); return { icon: svg, label: this.content, orientation: this.options.yAxisOrientation }; }; @@ -25941,9 +25941,9 @@ return /******/ (function(modules) { // webpackBootstrap var path, d; var svgHeight = Number(framework.svg.style.height.replace('px', '')); path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - path.setAttributeNS(null, "class", group.className); + path.setAttributeNS(null, 'class', group.className); if (group.style !== undefined) { - path.setAttributeNS(null, "style", group.style); + path.setAttributeNS(null, 'style', group.style); } // construct path from dataset @@ -26523,16 +26523,16 @@ return /******/ (function(modules) { // webpackBootstrap Legend.prototype._create = function () { this.dom.frame = document.createElement('div'); this.dom.frame.className = 'vis-legend'; - this.dom.frame.style.position = "absolute"; - this.dom.frame.style.top = "10px"; - this.dom.frame.style.display = "block"; + this.dom.frame.style.position = 'absolute'; + this.dom.frame.style.top = '10px'; + this.dom.frame.style.display = 'block'; this.dom.textArea = document.createElement('div'); this.dom.textArea.className = 'vis-legend-text'; - this.dom.textArea.style.position = "relative"; - this.dom.textArea.style.top = "0px"; + this.dom.textArea.style.position = 'relative'; + this.dom.textArea.style.top = '0px'; - this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); + this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.svg.style.position = 'absolute'; this.svg.style.top = 0 + 'px'; this.svg.style.width = this.options.iconSize + 5 + 'px'; @@ -26588,16 +26588,16 @@ return /******/ (function(modules) { // webpackBootstrap this.show(); if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { this.dom.frame.style.left = '4px'; - this.dom.frame.style.textAlign = "left"; - this.dom.textArea.style.textAlign = "left"; + this.dom.frame.style.textAlign = 'left'; + this.dom.textArea.style.textAlign = 'left'; this.dom.textArea.style.left = this.options.iconSize + 15 + 'px'; this.dom.textArea.style.right = ''; this.svg.style.left = 0 + 'px'; this.svg.style.right = ''; } else { this.dom.frame.style.right = '4px'; - this.dom.frame.style.textAlign = "right"; - this.dom.textArea.style.textAlign = "right"; + this.dom.frame.style.textAlign = 'right'; + this.dom.textArea.style.textAlign = 'right'; this.dom.textArea.style.right = this.options.iconSize + 15 + 'px'; this.dom.textArea.style.left = ''; this.svg.style.right = 0 + 'px'; @@ -26605,11 +26605,11 @@ return /******/ (function(modules) { // webpackBootstrap } if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { - this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px", "")) + 'px'; + this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace('px', '')) + 'px'; this.dom.frame.style.bottom = ''; } else { var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; - this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px", "")) + 'px'; + this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace('px', '')) + 'px'; this.dom.frame.style.top = ''; } @@ -26970,35 +26970,35 @@ return /******/ (function(modules) { // webpackBootstrap var _modulesPhysicsEngine2 = _interopRequireDefault(_modulesPhysicsEngine); - var _modulesClustering = __webpack_require__(100); + var _modulesClustering = __webpack_require__(99); var _modulesClustering2 = _interopRequireDefault(_modulesClustering); - var _modulesCanvasRenderer = __webpack_require__(102); + var _modulesCanvasRenderer = __webpack_require__(101); var _modulesCanvasRenderer2 = _interopRequireDefault(_modulesCanvasRenderer); - var _modulesCanvas = __webpack_require__(103); + var _modulesCanvas = __webpack_require__(102); var _modulesCanvas2 = _interopRequireDefault(_modulesCanvas); - var _modulesView = __webpack_require__(104); + var _modulesView = __webpack_require__(103); var _modulesView2 = _interopRequireDefault(_modulesView); - var _modulesInteractionHandler = __webpack_require__(106); + var _modulesInteractionHandler = __webpack_require__(105); var _modulesInteractionHandler2 = _interopRequireDefault(_modulesInteractionHandler); - var _modulesSelectionHandler = __webpack_require__(109); + var _modulesSelectionHandler = __webpack_require__(108); var _modulesSelectionHandler2 = _interopRequireDefault(_modulesSelectionHandler); - var _modulesLayoutEngine = __webpack_require__(110); + var _modulesLayoutEngine = __webpack_require__(109); var _modulesLayoutEngine2 = _interopRequireDefault(_modulesLayoutEngine); - var _modulesManipulationSystem = __webpack_require__(111); + var _modulesManipulationSystem = __webpack_require__(110); var _modulesManipulationSystem2 = _interopRequireDefault(_modulesManipulationSystem); @@ -27010,12 +27010,25 @@ return /******/ (function(modules) { // webpackBootstrap var _sharedValidator2 = _interopRequireDefault(_sharedValidator); - var _optionsJs = __webpack_require__(112); + var _optionsJs = __webpack_require__(111); - var _modulesKamadaKawaiJs = __webpack_require__(113); + var _modulesKamadaKawaiJs = __webpack_require__(112); var _modulesKamadaKawaiJs2 = _interopRequireDefault(_modulesKamadaKawaiJs); + __webpack_require__(114); + + var Emitter = __webpack_require__(12); + var Hammer = __webpack_require__(20); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(8); + var DataView = __webpack_require__(10); + var dotparser = __webpack_require__(115); + var gephiParser = __webpack_require__(116); + var Images = __webpack_require__(117); + var Activator = __webpack_require__(40); + var locales = __webpack_require__(118); + /** * @constructor Network * Create a network visualization, displaying nodes and edges. @@ -27027,19 +27040,6 @@ return /******/ (function(modules) { // webpackBootstrap * {Array} edges * @param {Object} options Options */ - __webpack_require__(115); - - var Emitter = __webpack_require__(12); - var Hammer = __webpack_require__(20); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(8); - var DataView = __webpack_require__(10); - var dotparser = __webpack_require__(116); - var gephiParser = __webpack_require__(117); - var Images = __webpack_require__(118); - var Activator = __webpack_require__(40); - var locales = __webpack_require__(119); - function Network(container, data, options) { var _this = this; @@ -27104,7 +27104,7 @@ return /******/ (function(modules) { // webpackBootstrap // setting up all modules this.images = new Images(function () { - return _this.body.emitter.emit("_requestRedraw"); + return _this.body.emitter.emit('_requestRedraw'); }); // object with images this.groups = new _modulesGroups2['default'](); // object with groups this.canvas = new _modulesCanvas2['default'](this.body); // DOM handler @@ -27120,8 +27120,8 @@ return /******/ (function(modules) { // webpackBootstrap this.nodesHandler = new _modulesNodesHandler2['default'](this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options this.edgesHandler = new _modulesEdgesHandler2['default'](this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options - this.body.modules["kamadaKawai"] = new _modulesKamadaKawaiJs2['default'](this.body, 150, 0.05); // Layouting algorithm. - this.body.modules["clustering"] = this.clustering; + this.body.modules['kamadaKawai'] = new _modulesKamadaKawaiJs2['default'](this.body, 150, 0.05); // Layouting algorithm. + this.body.modules['clustering'] = this.clustering; // create the DOM elements this.canvas._create(); @@ -27172,7 +27172,7 @@ return /******/ (function(modules) { // webpackBootstrap // reload the settings of the nodes to apply changes in groups that are not referenced by pointer. if (options.groups !== undefined) { - this.body.emitter.emit("refreshNodes"); + this.body.emitter.emit('refreshNodes'); } // these two do not have options at the moment, here for completeness //this.view.setOptions(options.view); @@ -27213,7 +27213,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this.activator === undefined) { this.activator = new Activator(this.canvas.frame); this.activator.on('change', function () { - _this2.body.emitter.emit("activate"); + _this2.body.emitter.emit('activate'); }); } } else { @@ -27221,15 +27221,15 @@ return /******/ (function(modules) { // webpackBootstrap this.activator.destroy(); delete this.activator; } - this.body.emitter.emit("activate"); + this.body.emitter.emit('activate'); } } else { - this.body.emitter.emit("activate"); + this.body.emitter.emit('activate'); } this.canvas.setSize(); // start the physics simulation. Can be safely called multiple times. - this.body.emitter.emit("startSimulation"); + this.body.emitter.emit('startSimulation'); } }; @@ -27267,23 +27267,23 @@ return /******/ (function(modules) { // webpackBootstrap var _this3 = this; // this event will trigger a rebuilding of the cache everything. Used when nodes or edges have been added or removed. - this.body.emitter.on("_dataChanged", function () { + this.body.emitter.on('_dataChanged', function () { // update shortcut lists _this3._updateVisibleIndices(); _this3.physics.updatePhysicsData(); - _this3.body.emitter.emit("_requestRedraw"); + _this3.body.emitter.emit('_requestRedraw'); // call the dataUpdated event because the only difference between the two is the updating of the indices - _this3.body.emitter.emit("_dataUpdated"); + _this3.body.emitter.emit('_dataUpdated'); }); // this is called when options of EXISTING nodes or edges have changed. - this.body.emitter.on("_dataUpdated", function () { + this.body.emitter.on('_dataUpdated', function () { // update values _this3._updateValueRange(_this3.body.nodes); _this3._updateValueRange(_this3.body.edges); // start simulation (can be called safely, even if already running) - _this3.body.emitter.emit("startSimulation"); - _this3.body.emitter.emit("_requestRedraw"); + _this3.body.emitter.emit('startSimulation'); + _this3.body.emitter.emit('_requestRedraw'); }); }; @@ -27299,8 +27299,8 @@ return /******/ (function(modules) { // webpackBootstrap */ Network.prototype.setData = function (data) { // reset the physics engine. - this.body.emitter.emit("resetPhysics"); - this.body.emitter.emit("_resetData"); + this.body.emitter.emit('resetPhysics'); + this.body.emitter.emit('_resetData'); // unselect all to ensure no selections from old data are carried over. this.selectionHandler.unselectAll(); @@ -27330,13 +27330,13 @@ return /******/ (function(modules) { // webpackBootstrap } // emit change in data - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); // emit data loaded - this.body.emitter.emit("_dataLoaded"); + this.body.emitter.emit('_dataLoaded'); // find a stable position or start animating to a stable position - this.body.emitter.emit("initPhysics"); + this.body.emitter.emit('initPhysics'); }; /** @@ -27346,7 +27346,7 @@ return /******/ (function(modules) { // webpackBootstrap * network = null; */ Network.prototype.destroy = function () { - this.body.emitter.emit("destroy"); + this.body.emitter.emit('destroy'); // clear events this.body.emitter.off(); this.off(); @@ -27471,7 +27471,7 @@ return /******/ (function(modules) { // webpackBootstrap return this.manipulation.editNode.apply(this.manipulation, arguments); }; Network.prototype.editNodeMode = function () { - console.log("Deprecated: Please use editNode instead of editNodeMode.");return this.manipulation.editNode.apply(this.manipulation, arguments); + console.log('Deprecated: Please use editNode instead of editNodeMode.');return this.manipulation.editNode.apply(this.manipulation, arguments); }; Network.prototype.addEdgeMode = function () { return this.manipulation.addEdgeMode.apply(this.manipulation, arguments); @@ -27642,7 +27642,7 @@ return /******/ (function(modules) { // webpackBootstrap _createClass(Groups, [{ key: "setOptions", value: function setOptions(options) { - var optionFields = ['useDefaultGroups']; + var optionFields = ["useDefaultGroups"]; if (options !== undefined) { for (var groupName in options) { @@ -27655,16 +27655,18 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "clear", /** * Clear all groups */ - }, { - key: "clear", value: function clear() { this.groups = {}; this.groupsArray = []; } + }, { + key: "get", /** * get group options of a groupname. If groupname is not found, a new group @@ -27672,8 +27674,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {*} groupname Can be a number, string, Date, etc. * @return {Object} group The created group, containing all group options */ - }, { - key: "get", value: function get(groupname) { var group = this.groups[groupname]; if (group === undefined) { @@ -27696,6 +27696,8 @@ return /******/ (function(modules) { // webpackBootstrap return group; } + }, { + key: "add", /** * Add a custom group style @@ -27704,8 +27706,6 @@ return /******/ (function(modules) { // webpackBootstrap * backgroundColor, etc. * @return {Object} group The created group object */ - }, { - key: "add", value: function add(groupName, style) { this.groups[groupName] = style; this.groupsArray.push(groupName); @@ -27917,14 +27917,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'setData', /** * Set a data set with nodes for the network * @param {Array | DataSet | DataView} nodes The data containing the nodes. * @private */ - }, { - key: 'setData', value: function setData(nodes) { var _this3 = this; @@ -27968,17 +27968,17 @@ return /******/ (function(modules) { // webpackBootstrap } if (doNotEmit === false) { - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } } + }, { + key: 'add', /** * Add nodes * @param {Number[] | String[]} ids * @private */ - }, { - key: 'add', value: function add(ids) { var doNotEmit = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -27995,17 +27995,17 @@ return /******/ (function(modules) { // webpackBootstrap this.layoutEngine.positionInitially(newNodes); if (doNotEmit === false) { - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } } + }, { + key: 'update', /** * Update existing nodes, or create them when not yet existing * @param {Number[] | String[]} ids * @private */ - }, { - key: 'update', value: function update(ids, changedData) { var nodes = this.body.nodes; var dataChanged = false; @@ -28024,19 +28024,19 @@ return /******/ (function(modules) { // webpackBootstrap } } if (dataChanged === true) { - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } else { - this.body.emitter.emit("_dataUpdated"); + this.body.emitter.emit('_dataUpdated'); } } + }, { + key: 'remove', /** * Remove existing nodes. If nodes do not exist, the method will just ignore it. * @param {Number[] | String[]} ids * @private */ - }, { - key: 'remove', value: function remove(ids) { var nodes = this.body.nodes; @@ -28045,16 +28045,16 @@ return /******/ (function(modules) { // webpackBootstrap delete nodes[id]; } - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } + }, { + key: 'create', /** * create a node * @param properties * @param constructorClass */ - }, { - key: 'create', value: function create(properties) { var constructorClass = arguments.length <= 1 || arguments[1] === undefined ? _componentsNode2['default'] : arguments[1]; @@ -28081,14 +28081,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'getPositions', /** * Returns the positions of the nodes. * @param ids --> optional, can be array of nodeIds, can be string * @returns {{}} */ - }, { - key: 'getPositions', value: function getPositions(ids) { var dataArray = {}; if (ids !== undefined) { @@ -28113,12 +28113,12 @@ return /******/ (function(modules) { // webpackBootstrap } return dataArray; } + }, { + key: 'storePositions', /** * Load the XY positions of the nodes into the dataset. */ - }, { - key: 'storePositions', value: function storePositions() { // todo: add support for clusters and hierarchical. var dataArray = []; @@ -28134,27 +28134,27 @@ return /******/ (function(modules) { // webpackBootstrap } dataset.update(dataArray); } + }, { + key: 'getBoundingBox', /** * get the bounding box of a node. * @param nodeId * @returns {j|*} */ - }, { - key: 'getBoundingBox', value: function getBoundingBox(nodeId) { if (this.body.nodes[nodeId] !== undefined) { return this.body.nodes[nodeId].shape.boundingBox; } } + }, { + key: 'getConnectedNodes', /** * Get the Ids of nodes connected to this node. * @param nodeId * @returns {Array} */ - }, { - key: 'getConnectedNodes', value: function getConnectedNodes(nodeId) { var nodeList = []; if (this.body.nodes[nodeId] !== undefined) { @@ -28179,14 +28179,14 @@ return /******/ (function(modules) { // webpackBootstrap } return nodeList; } + }, { + key: 'getConnectedEdges', /** * Get the ids of the edges connected to this node. * @param nodeId * @returns {*} */ - }, { - key: 'getConnectedEdges', value: function getConnectedEdges(nodeId) { var edgeList = []; if (this.body.nodes[nodeId] !== undefined) { @@ -28195,10 +28195,12 @@ return /******/ (function(modules) { // webpackBootstrap edgeList.push(node.edges[i].id); } } else { - console.log("NodeId provided for getConnectedEdges does not exist. Provided: ", nodeId); + console.log('NodeId provided for getConnectedEdges does not exist. Provided: ', nodeId); } return edgeList; } + }, { + key: 'moveNode', /** * Move a node. @@ -28206,8 +28208,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param Number x * @param Number y */ - }, { - key: 'moveNode', value: function moveNode(nodeId, x, y) { var _this4 = this; @@ -28215,10 +28215,10 @@ return /******/ (function(modules) { // webpackBootstrap this.body.nodes[nodeId].x = Number(x); this.body.nodes[nodeId].y = Number(y); setTimeout(function () { - _this4.body.emitter.emit("startSimulation"); + _this4.body.emitter.emit('startSimulation'); }, 0); } else { - console.log("Node id supplied to moveNode does not exist. Provided: ", nodeId); + console.log('Node id supplied to moveNode does not exist. Provided: ', nodeId); } } }]); @@ -28365,39 +28365,39 @@ return /******/ (function(modules) { // webpackBootstrap this.setOptions(options); } - /** - * Attach a edge to the node - * @param {Edge} edge - */ - _createClass(Node, [{ key: 'attachEdge', + + /** + * Attach a edge to the node + * @param {Edge} edge + */ value: function attachEdge(edge) { if (this.edges.indexOf(edge) === -1) { this.edges.push(edge); } } + }, { + key: 'detachEdge', /** * Detach a edge from the node * @param {Edge} edge */ - }, { - key: 'detachEdge', value: function detachEdge(edge) { var index = this.edges.indexOf(edge); if (index != -1) { this.edges.splice(index, 1); } } + }, { + key: 'setOptions', /** * Set or overwrite options for the node * @param {Object} options an object with options * @param {Object} constants and object with default, global options */ - }, { - key: 'setOptions', value: function setOptions(options) { var currentShape = this.options.shape; if (!options) { @@ -28409,7 +28409,7 @@ return /******/ (function(modules) { // webpackBootstrap } if (this.id === undefined) { - throw "Node must have an id"; + throw 'Node must have an id'; } // set these options locally @@ -28451,7 +28451,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this.imagelist) { this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage, this.id); } else { - throw "No imagelist provided"; + throw 'No imagelist provided'; } } @@ -28463,13 +28463,6 @@ return /******/ (function(modules) { // webpackBootstrap } return false; } - - /** - * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined. - * Static so it can also be used by the handler. - * @param parentOptions - * @param newOptions - */ }, { key: 'updateLabelModule', value: function updateLabelModule() { @@ -28538,48 +28531,50 @@ return /******/ (function(modules) { // webpackBootstrap } this._reset(); } + }, { + key: 'select', /** * select this node */ - }, { - key: 'select', value: function select() { this.selected = true; this._reset(); } + }, { + key: 'unselect', /** * unselect this node */ - }, { - key: 'unselect', value: function unselect() { this.selected = false; this._reset(); } + }, { + key: '_reset', /** * Reset the calculated size of the node, forces it to recalculate its size * @private */ - }, { - key: '_reset', value: function _reset() { this.shape.width = undefined; this.shape.height = undefined; } + }, { + key: 'getTitle', /** * get the title of this node. * @return {string} title The title of the node, or undefined when no title * has been set. */ - }, { - key: 'getTitle', value: function getTitle() { return this.options.title; } + }, { + key: 'distanceToBorder', /** * Calculate the distance to the border of the Node @@ -28587,41 +28582,41 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} angle Angle in radians * @returns {number} distance Distance to the border in pixels */ - }, { - key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { return this.shape.distanceToBorder(ctx, angle); } + }, { + key: 'isFixed', /** * Check if this node has a fixed x and y position * @return {boolean} true if fixed, false if not */ - }, { - key: 'isFixed', value: function isFixed() { return this.options.fixed.x && this.options.fixed.y; } + }, { + key: 'isSelected', /** * check if this node is selecte * @return {boolean} selected True if node is selected, else false */ - }, { - key: 'isSelected', value: function isSelected() { return this.selected; } + }, { + key: 'getValue', /** * Retrieve the value of the node. Can be undefined * @return {Number} value */ - }, { - key: 'getValue', value: function getValue() { return this.options.value; } + }, { + key: 'setValueRange', /** * Adjust the value range of the node. The node will adjust it's size @@ -28629,8 +28624,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} min * @param {Number} max */ - }, { - key: 'setValueRange', value: function setValueRange(min, max, total) { if (this.options.value !== undefined) { var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); @@ -28645,61 +28638,68 @@ return /******/ (function(modules) { // webpackBootstrap this.options.font.size = this.baseFontSize; } } + }, { + key: 'draw', /** * Draw this node in the given canvas * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * @param {CanvasRenderingContext2D} ctx */ - }, { - key: 'draw', value: function draw(ctx) { this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); } + }, { + key: 'updateBoundingBox', /** * Update the bounding box of the shape */ - }, { - key: 'updateBoundingBox', value: function updateBoundingBox(ctx) { this.shape.updateBoundingBox(this.x, this.y, ctx); } + }, { + key: 'resize', /** * Recalculate the size of this node in the given canvas * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * @param {CanvasRenderingContext2D} ctx */ - }, { - key: 'resize', value: function resize(ctx) { this.shape.resize(ctx, this.selected); } + }, { + key: 'isOverlappingWith', /** * Check if this object is overlapping with the provided object * @param {Object} obj an object with parameters left, top, right, bottom * @return {boolean} True if location is located on node */ - }, { - key: 'isOverlappingWith', value: function isOverlappingWith(obj) { return this.shape.left < obj.right && this.shape.left + this.shape.width > obj.left && this.shape.top < obj.bottom && this.shape.top + this.shape.height > obj.top; } + }, { + key: 'isBoundingBoxOverlappingWith', /** * Check if this object is overlapping with the provided object * @param {Object} obj an object with parameters left, top, right, bottom * @return {boolean} True if location is located on node */ - }, { - key: 'isBoundingBoxOverlappingWith', value: function isBoundingBoxOverlappingWith(obj) { return this.shape.boundingBox.left < obj.right && this.shape.boundingBox.right > obj.left && this.shape.boundingBox.top < obj.bottom && this.shape.boundingBox.bottom > obj.top; } }], [{ key: 'parseOptions', + + /** + * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined. + * Static so it can also be used by the handler. + * @param parentOptions + * @param newOptions + */ value: function parseOptions(parentOptions, newOptions) { var allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; var globalOptions = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3]; @@ -28839,16 +28839,16 @@ return /******/ (function(modules) { // webpackBootstrap // draw text this._drawText(ctx, selected, x, y, baseline); } + }, { + key: '_drawBackground', /** * Draws the label background * @param {CanvasRenderingContext2D} ctx * @private */ - }, { - key: '_drawBackground', value: function _drawBackground(ctx) { - if (this.fontOptions.background !== undefined && this.fontOptions.background !== "none") { + if (this.fontOptions.background !== undefined && this.fontOptions.background !== 'none') { ctx.fillStyle = this.fontOptions.background; var lineMargin = 2; @@ -28869,6 +28869,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_drawText', /** * @@ -28877,8 +28879,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param baseline * @private */ - }, { - key: '_drawText', value: function _drawText(ctx, selected, x, y) { var baseline = arguments.length <= 4 || arguments[4] === undefined ? 'middle' : arguments[4]; @@ -28898,15 +28898,15 @@ return /******/ (function(modules) { // webpackBootstrap var fontColor = _getColor22[0]; var strokeColor = _getColor22[1]; - // configure context for drawing the text - var _setAlignment2 = this._setAlignment(ctx, x, yLine, baseline); var _setAlignment22 = _slicedToArray(_setAlignment2, 2); x = _setAlignment22[0]; yLine = _setAlignment22[1]; - ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + fontSize + "px " + this.fontOptions.face; + + // configure context for drawing the text + ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + fontSize + 'px ' + this.fontOptions.face; ctx.fillStyle = fontColor; ctx.textAlign = 'center'; @@ -28940,17 +28940,19 @@ return /******/ (function(modules) { // webpackBootstrap ctx.textBaseline = 'alphabetic'; yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers } else if (this.fontOptions.align === 'bottom') { - ctx.textBaseline = 'hanging'; - yLine += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers - } else { - ctx.textBaseline = 'middle'; - } + ctx.textBaseline = 'hanging'; + yLine += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers + } else { + ctx.textBaseline = 'middle'; + } } else { ctx.textBaseline = baseline; } return [x, yLine]; } + }, { + key: '_getColor', /** * fade in when relative scale is between threshold and threshold - 1. @@ -28960,8 +28962,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*[]} * @private */ - }, { - key: '_getColor', value: function _getColor(viewFontSize) { var fontColor = this.fontOptions.color || '#000000'; var strokeColor = this.fontOptions.strokeColor || '#ffffff'; @@ -28972,6 +28972,8 @@ return /******/ (function(modules) { // webpackBootstrap } return [fontColor, strokeColor]; } + }, { + key: 'getTextSize', /** * @@ -28979,8 +28981,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param selected * @returns {{width: number, height: number}} */ - }, { - key: 'getTextSize', value: function getTextSize(ctx) { var selected = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -28991,6 +28991,8 @@ return /******/ (function(modules) { // webpackBootstrap }; return size; } + }, { + key: 'calculateLabelSize', /** * @@ -29000,8 +29002,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param y * @param baseline */ - }, { - key: 'calculateLabelSize', value: function calculateLabelSize(ctx, selected) { var x = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2]; var y = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; @@ -29014,7 +29014,7 @@ return /******/ (function(modules) { // webpackBootstrap this.size.left = x - this.size.width * 0.5; this.size.top = y - this.size.height * 0.5; this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size; - if (baseline === "hanging") { + if (baseline === 'hanging') { this.size.top += 0.5 * this.fontOptions.size; this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers this.size.yLine += 4; // distance from node @@ -29022,6 +29022,8 @@ return /******/ (function(modules) { // webpackBootstrap this.labelDirty = false; } + }, { + key: '_processLabel', /** * This calculates the width as well as explodes the label string and calculates the amount of lines. @@ -29030,8 +29032,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: '_processLabel', value: function _processLabel(ctx, selected) { var width = 0; var lines = ['']; @@ -29039,7 +29039,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this.nodeOptions.label !== undefined) { lines = String(this.nodeOptions.label).split('\n'); lineCount = lines.length; - ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + this.fontOptions.size + "px " + this.fontOptions.face; + ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + this.fontOptions.size + 'px ' + this.fontOptions.face; width = ctx.measureText(lines[0]).width; for (var i = 1; i < lineCount; i++) { var lineWidth = ctx.measureText(lines[i]).width; @@ -29057,8 +29057,8 @@ return /******/ (function(modules) { // webpackBootstrap var allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; if (typeof newOptions.font === 'string') { - var newOptionsArray = newOptions.font.split(" "); - parentOptions.size = newOptionsArray[0].replace("px", ''); + var newOptionsArray = newOptions.font.split(' '); + parentOptions.size = newOptionsArray[0].replace('px', ''); parentOptions.face = newOptionsArray[1]; parentOptions.color = newOptionsArray[2]; } else if (typeof newOptions.font === 'object') { @@ -29092,7 +29092,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -29259,7 +29259,7 @@ return /******/ (function(modules) { // webpackBootstrap } ctx.setLineDash(dashes); } else { - console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); + console.warn('setLineDash is not supported in this browser. The dashed borders cannot be used.'); this.options.shapeProperties.borderDashes = false; } } @@ -29271,7 +29271,7 @@ return /******/ (function(modules) { // webpackBootstrap if (ctx.setLineDash !== undefined) { ctx.setLineDash([0]); } else { - console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); + console.warn('setLineDash is not supported in this browser. The dashed borders cannot be used.'); this.options.shapeProperties.borderDashes = false; } } @@ -29302,7 +29302,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilCircleImageBase = __webpack_require__(67); @@ -29392,7 +29392,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -29417,6 +29417,8 @@ return /******/ (function(modules) { // webpackBootstrap this.imageObj = imageObj; } } + }, { + key: '_resizeImage', /** * This function resizes the image by the options size when the image has not yet loaded. If the image has loaded, we @@ -29424,8 +29426,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: '_resizeImage', value: function _resizeImage() { var force = false; if (!this.imageObj.width || !this.imageObj.height) { @@ -29563,7 +29563,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilCircleImageBase = __webpack_require__(67); @@ -29669,7 +29669,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -29782,7 +29782,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -29838,7 +29838,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -29944,7 +29944,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30001,7 +30001,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -30117,7 +30117,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -30184,12 +30184,12 @@ return /******/ (function(modules) { // webpackBootstrap var iconSize = Number(this.options.icon.size); if (this.options.icon.code !== undefined) { - ctx.font = (selected ? "bold " : "") + iconSize + "px " + this.options.icon.face; + ctx.font = (selected ? 'bold ' : '') + iconSize + 'px ' + this.options.icon.face; // draw icon - ctx.fillStyle = this.options.icon.color || "black"; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; + ctx.fillStyle = this.options.icon.color || 'black'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; // draw shadow if enabled this.enableShadow(ctx); @@ -30232,7 +30232,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilCircleImageBase = __webpack_require__(67); @@ -30352,7 +30352,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30408,7 +30408,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30464,7 +30464,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilNodeBase = __webpack_require__(65); @@ -30550,7 +30550,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30606,7 +30606,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilShapeBase = __webpack_require__(71); @@ -30756,7 +30756,7 @@ return /******/ (function(modules) { // webpackBootstrap }, smooth: { enabled: true, - type: "dynamic", + type: 'dynamic', forceDirection: 'none', roundness: 0.5 }, @@ -30776,7 +30776,7 @@ return /******/ (function(modules) { // webpackBootstrap var _this2 = this; // this allows external modules to force all dynamic curves to turn static. - this.body.emitter.on("_forceDisableDynamicCurves", function (type) { + this.body.emitter.on('_forceDisableDynamicCurves', function (type) { if (type === 'dynamic') { type = 'continuous'; } @@ -30804,20 +30804,20 @@ return /******/ (function(modules) { // webpackBootstrap } } if (emitChange === true) { - _this2.body.emitter.emit("_dataChanged"); + _this2.body.emitter.emit('_dataChanged'); } }); // this is called when options of EXISTING nodes or edges have changed. - this.body.emitter.on("_dataUpdated", function () { + this.body.emitter.on('_dataUpdated', function () { _this2.reconnectEdges(); _this2.markAllEdgesAsDirty(); }); // refresh the edges. Used when reverting from hierarchical layout - this.body.emitter.on("refreshEdges", this.refresh.bind(this)); - this.body.emitter.on("refresh", this.refresh.bind(this)); - this.body.emitter.on("destroy", function () { + this.body.emitter.on('refreshEdges', this.refresh.bind(this)); + this.body.emitter.on('refresh', this.refresh.bind(this)); + this.body.emitter.on('destroy', function () { delete _this2.body.functions.createEdge; delete _this2.edgesListeners.add; delete _this2.edgesListeners.update; @@ -30864,6 +30864,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'setData', /** * Load edges by reading the data table @@ -30871,8 +30873,6 @@ return /******/ (function(modules) { // webpackBootstrap * @private * @private */ - }, { - key: 'setData', value: function setData(edges) { var _this3 = this; @@ -30915,17 +30915,17 @@ return /******/ (function(modules) { // webpackBootstrap } if (doNotEmit === false) { - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } } + }, { + key: 'add', /** * Add edges * @param {Number[] | String[]} ids * @private */ - }, { - key: 'add', value: function add(ids) { var doNotEmit = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -30940,22 +30940,22 @@ return /******/ (function(modules) { // webpackBootstrap oldEdge.disconnect(); } - var data = edgesData.get(id, { "showInternalIds": true }); + var data = edgesData.get(id, { 'showInternalIds': true }); edges[id] = this.create(data); } if (doNotEmit === false) { - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } } + }, { + key: 'update', /** * Update existing edges, or create them when not yet existing * @param {Number[] | String[]} ids * @private */ - }, { - key: 'update', value: function update(ids) { var edges = this.body.edges; var edgesData = this.body.data.edges; @@ -30977,19 +30977,19 @@ return /******/ (function(modules) { // webpackBootstrap } if (dataChanged === true) { - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } else { - this.body.emitter.emit("_dataUpdated"); + this.body.emitter.emit('_dataUpdated'); } } + }, { + key: 'remove', /** * Remove existing edges. Non existing ids will be ignored * @param {Number[] | String[]} ids * @private */ - }, { - key: 'remove', value: function remove(ids) { var edges = this.body.edges; for (var i = 0; i < ids.length; i++) { @@ -31002,7 +31002,7 @@ return /******/ (function(modules) { // webpackBootstrap } } - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } }, { key: 'refresh', @@ -31031,13 +31031,13 @@ return /******/ (function(modules) { // webpackBootstrap this.body.edges[edgeId].edgeType.colorDirty = true; } } + }, { + key: 'reconnectEdges', /** * Reconnect all edges * @private */ - }, { - key: 'reconnectEdges', value: function reconnectEdges() { var id; var nodes = this.body.nodes; @@ -31117,6 +31117,8 @@ return /******/ (function(modules) { // webpackBootstrap var _edgesStraightEdge2 = _interopRequireDefault(_edgesStraightEdge); + var util = __webpack_require__(1); + /** * @class Edge * @@ -31132,14 +31134,13 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Object} constants An object with default values for * example for the color */ - var util = __webpack_require__(1); var Edge = (function () { function Edge(options, body, globalOptions) { _classCallCheck(this, Edge); if (body === undefined) { - throw "No body provided"; + throw 'No body provided'; } this.options = util.bridgeObject(globalOptions); this.globalOptions = globalOptions; @@ -31169,14 +31170,14 @@ return /******/ (function(modules) { // webpackBootstrap this.setOptions(options); } - /** - * Set or overwrite options for the edge - * @param {Object} options an object with options - * @param doNotEmit - */ - _createClass(Edge, [{ key: 'setOptions', + + /** + * Set or overwrite options for the edge + * @param {Object} options an object with options + * @param doNotEmit + */ value: function setOptions(options) { if (!options) { return; @@ -31220,7 +31221,6 @@ return /******/ (function(modules) { // webpackBootstrap } }, { key: 'updateLabelModule', - // this sets the pointer of the option back to the global option. /** * update the options in the label module @@ -31231,13 +31231,13 @@ return /******/ (function(modules) { // webpackBootstrap this.baseFontSize = this.labelModule.baseSize; } } + }, { + key: 'updateEdgeType', /** * update the edge type, set the options * @returns {boolean} */ - }, { - key: 'updateEdgeType', value: function updateEdgeType() { var dataChanged = false; var changeInType = true; @@ -31281,12 +31281,12 @@ return /******/ (function(modules) { // webpackBootstrap return dataChanged; } + }, { + key: 'connect', /** * Connect an edge to its nodes */ - }, { - key: 'connect', value: function connect() { this.disconnect(); @@ -31308,12 +31308,12 @@ return /******/ (function(modules) { // webpackBootstrap this.edgeType.connect(); } + }, { + key: 'disconnect', /** * Disconnect an edge from its nodes */ - }, { - key: 'disconnect', value: function disconnect() { if (this.from) { this.from.detachEdge(this); @@ -31326,37 +31326,39 @@ return /******/ (function(modules) { // webpackBootstrap this.connected = false; } + }, { + key: 'getTitle', /** * get the title of this edge. * @return {string} title The title of the edge, or undefined when no title * has been set. */ - }, { - key: 'getTitle', value: function getTitle() { return this.title; } + }, { + key: 'isSelected', /** * check if this node is selecte * @return {boolean} selected True if node is selected, else false */ - }, { - key: 'isSelected', value: function isSelected() { return this.selected; } + }, { + key: 'getValue', /** * Retrieve the value of the edge. Can be undefined * @return {Number} value */ - }, { - key: 'getValue', value: function getValue() { return this.options.value; } + }, { + key: 'setValueRange', /** * Adjust the value range of the edge. The edge will adjust it's width @@ -31365,8 +31367,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} max * @param total */ - }, { - key: 'setValueRange', value: function setValueRange(min, max, total) { if (this.options.value !== undefined) { var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); @@ -31398,6 +31398,8 @@ return /******/ (function(modules) { // webpackBootstrap this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width; } } + }, { + key: 'draw', /** * Redraw a edge @@ -31405,8 +31407,6 @@ return /******/ (function(modules) { // webpackBootstrap * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * @param {CanvasRenderingContext2D} ctx */ - }, { - key: 'draw', value: function draw(ctx) { var via = this.edgeType.drawLine(ctx, this.selected, this.hover); this.drawArrows(ctx, via); @@ -31439,7 +31439,7 @@ return /******/ (function(modules) { // webpackBootstrap ctx.save(); // if the label has to be rotated: - if (this.options.font.align !== "horizontal") { + if (this.options.font.align !== 'horizontal') { this.labelModule.calculateLabelSize(ctx, selected, point.x, point.y); ctx.translate(point.x, this.labelModule.size.yLine); this._rotateForLabelAlignment(ctx); @@ -31465,14 +31465,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'isOverlappingWith', /** * Check if this object is overlapping with the provided object * @param {Object} obj an object with parameters left, top * @return {boolean} True if location is located on the edge */ - }, { - key: 'isOverlappingWith', value: function isOverlappingWith(obj) { if (this.connected) { var distMax = 10; @@ -31490,14 +31490,14 @@ return /******/ (function(modules) { // webpackBootstrap return false; } } + }, { + key: '_rotateForLabelAlignment', /** * Rotates the canvas so the text is most readable * @param {CanvasRenderingContext2D} ctx * @private */ - }, { - key: '_rotateForLabelAlignment', value: function _rotateForLabelAlignment(ctx) { var dy = this.from.y - this.to.y; var dx = this.from.x - this.to.x; @@ -31510,6 +31510,8 @@ return /******/ (function(modules) { // webpackBootstrap ctx.rotate(angleInDegrees); } + }, { + key: '_pointOnCircle', /** * Get a point on a circle @@ -31520,8 +31522,6 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Object} point * @private */ - }, { - key: '_pointOnCircle', value: function _pointOnCircle(x, y, radius, percentage) { var angle = percentage * 2 * Math.PI; return { @@ -31539,13 +31539,13 @@ return /******/ (function(modules) { // webpackBootstrap value: function unselect() { this.selected = false; } + }, { + key: 'cleanup', /** * cleans all required things on delete * @returns {*} */ - }, { - key: 'cleanup', value: function cleanup() { return this.edgeType.cleanup(); } @@ -31586,13 +31586,13 @@ return /******/ (function(modules) { // webpackBootstrap if (newOptions.arrows !== undefined && newOptions.arrows !== null) { if (typeof newOptions.arrows === 'string') { var arrows = newOptions.arrows.toLowerCase(); - if (arrows.indexOf("to") != -1) { + if (arrows.indexOf('to') != -1) { parentOptions.arrows.to.enabled = true; } - if (arrows.indexOf("middle") != -1) { + if (arrows.indexOf('middle') != -1) { parentOptions.arrows.middle.enabled = true; } - if (arrows.indexOf("from") != -1) { + if (arrows.indexOf('from') != -1) { parentOptions.arrows.from.enabled = true; } } else if (typeof newOptions.arrows === 'object') { @@ -31600,7 +31600,7 @@ return /******/ (function(modules) { // webpackBootstrap util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'middle', allowDeletion, globalOptions.arrows); util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'from', allowDeletion, globalOptions.arrows); } else { - throw new Error("The arrow newOptions can only be an object or a string. Refer to the documentation. You used:" + JSON.stringify(newOptions.arrows)); + throw new Error('The arrow newOptions can only be an object or a string. Refer to the documentation. You used:' + JSON.stringify(newOptions.arrows)); } } else if (allowDeletion === true && newOptions.arrows === null) { parentOptions.arrows = Object.create(globalOptions.arrows); // this sets the pointer of the option back to the global option. @@ -31643,7 +31643,7 @@ return /******/ (function(modules) { // webpackBootstrap if (newOptions.font !== undefined && newOptions.font !== null) { _sharedLabel2['default'].parseOptions(parentOptions.font, newOptions); } else if (allowDeletion === true && newOptions.font === null) { - parentOptions.font = Object.create(globalOptions.font); + parentOptions.font = Object.create(globalOptions.font); // this sets the pointer of the option back to the global option. } } }]); @@ -31674,7 +31674,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilCubicBezierEdgeBase = __webpack_require__(84); @@ -31689,14 +31689,14 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(CubicBezierEdge.prototype), 'constructor', this).call(this, options, body, labelModule); } - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - _createClass(CubicBezierEdge, [{ key: '_line', + + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ value: function _line(ctx) { // get the coordinates of the support points. @@ -31770,6 +31770,8 @@ return /******/ (function(modules) { // webpackBootstrap // x3,y3 is the point return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2); } + }, { + key: 'getPoint', /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way @@ -31778,8 +31780,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ - }, { - key: 'getPoint', value: function getPoint(percentage) { var _ref3 = arguments.length <= 1 || arguments[1] === undefined ? this._getViaCoordinates() : arguments[1]; @@ -31825,7 +31825,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _BezierEdgeBase2 = __webpack_require__(85); @@ -31840,22 +31840,22 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(CubicBezierEdgeBase.prototype), 'constructor', this).call(this, options, body, labelModule); } - /** - * Calculate the distance between a point (x3,y3) and a line segment from - * (x1,y1) to (x2,y2). - * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment - * https://en.wikipedia.org/wiki/B%C3%A9zier_curve - * @param {number} x1 from x - * @param {number} y1 from y - * @param {number} x2 to x - * @param {number} y2 to y - * @param {number} x3 point to check x - * @param {number} y3 point to check y - * @private - */ - _createClass(CubicBezierEdgeBase, [{ key: '_getDistanceToBezierEdge', + + /** + * Calculate the distance between a point (x3,y3) and a line segment from + * (x1,y1) to (x2,y2). + * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment + * https://en.wikipedia.org/wiki/B%C3%A9zier_curve + * @param {number} x1 from x + * @param {number} y1 from y + * @param {number} x2 to x + * @param {number} y2 to y + * @param {number} x3 point to check x + * @param {number} y3 point to check y + * @private + */ value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2) { // x3,y3 is the point var minDistance = 1e9; @@ -31911,7 +31911,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _EdgeBase2 = __webpack_require__(86); @@ -31926,22 +31926,22 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(BezierEdgeBase.prototype), 'constructor', this).call(this, options, body, labelModule); } - /** - * This function uses binary search to look for the point where the bezier curve crosses the border of the node. - * - * @param nearNode - * @param ctx - * @param viaNode - * @param nearNode - * @param ctx - * @param viaNode - * @param nearNode - * @param ctx - * @param viaNode - */ - _createClass(BezierEdgeBase, [{ key: '_findBorderPositionBezier', + + /** + * This function uses binary search to look for the point where the bezier curve crosses the border of the node. + * + * @param nearNode + * @param ctx + * @param viaNode + * @param nearNode + * @param ctx + * @param viaNode + * @param nearNode + * @param ctx + * @param viaNode + */ value: function _findBorderPositionBezier(nearNode, ctx) { var viaNode = arguments.length <= 2 || arguments[2] === undefined ? this._getViaCoordinates() : arguments[2]; @@ -31969,19 +31969,19 @@ return /******/ (function(modules) { // webpackBootstrap if (Math.abs(difference) < threshold) { break; // found } else if (difference < 0) { - // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (from === false) { - low = middle; - } else { - high = middle; - } + // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (from === false) { + low = middle; } else { - if (from === false) { - high = middle; - } else { - low = middle; - } + high = middle; } + } else { + if (from === false) { + high = middle; + } else { + low = middle; + } + } iteration++; } @@ -31989,6 +31989,8 @@ return /******/ (function(modules) { // webpackBootstrap return pos; } + }, { + key: '_getDistanceToBezierEdge', /** * Calculate the distance between a point (x3,y3) and a line segment from @@ -32002,8 +32004,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {number} y3 point to check y * @private */ - }, { - key: '_getDistanceToBezierEdge', value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via) { // x3,y3 is the point var minDistance = 1e9; @@ -32086,6 +32086,8 @@ return /******/ (function(modules) { // webpackBootstrap this.to = this.body.nodes[this.options.to]; this.id = this.options.id; } + }, { + key: 'drawLine', /** * Redraw a edge as a line @@ -32094,8 +32096,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {CanvasRenderingContext2D} ctx * @private */ - }, { - key: 'drawLine', value: function drawLine(ctx, selected, hover) { // set style ctx.strokeStyle = this.getColor(ctx, selected, hover); @@ -32247,6 +32247,8 @@ return /******/ (function(modules) { // webpackBootstrap } return [x, y, radius]; } + }, { + key: '_pointOnCircle', /** * Get a point on a circle @@ -32257,8 +32259,6 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Object} point * @private */ - }, { - key: '_pointOnCircle', value: function _pointOnCircle(x, y, radius, percentage) { var angle = percentage * 2 * Math.PI; return { @@ -32266,6 +32266,8 @@ return /******/ (function(modules) { // webpackBootstrap y: y - radius * Math.sin(angle) }; } + }, { + key: '_findBorderPositionCircle', /** * This function uses binary search to look for the point where the circle crosses the border of the node. @@ -32275,8 +32277,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ - }, { - key: '_findBorderPositionCircle', value: function _findBorderPositionCircle(node, ctx, options) { var x = options.x; var y = options.y; @@ -32306,25 +32306,27 @@ return /******/ (function(modules) { // webpackBootstrap if (Math.abs(difference) < threshold) { break; // found } else if (difference > 0) { - // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (direction > 0) { - low = middle; - } else { - high = middle; - } + // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (direction > 0) { + low = middle; } else { - if (direction > 0) { - high = middle; - } else { - low = middle; - } + high = middle; + } + } else { + if (direction > 0) { + high = middle; + } else { + low = middle; } + } iteration++; } pos.t = middle; return pos; } + }, { + key: 'getLineWidth', /** * Get the line width of the edge. Depends on width and whether one of the @@ -32332,8 +32334,6 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number} width * @private */ - }, { - key: 'getLineWidth', value: function getLineWidth(selected, hover) { if (selected === true) { return Math.max(this.selectionWidth, 0.3 / this.body.view.scale); @@ -32374,7 +32374,7 @@ return /******/ (function(modules) { // webpackBootstrap } if (this.colorDirty === true) { - if (colorOptions.inherit === "to") { + if (colorOptions.inherit === 'to') { this.color.highlight = this.to.options.color.highlight.border; this.color.hover = this.to.options.color.hover.border; this.color.color = util.overrideOpacity(this.to.options.color.border, colorOptions.opacity); @@ -32402,6 +32402,8 @@ return /******/ (function(modules) { // webpackBootstrap return this.color.color; } } + }, { + key: '_circle', /** * Draw a line from a node to itself, a circle @@ -32411,8 +32413,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} radius * @private */ - }, { - key: '_circle', value: function _circle(ctx, x, y, radius) { // draw shadow if enabled this.enableShadow(ctx); @@ -32425,6 +32425,8 @@ return /******/ (function(modules) { // webpackBootstrap // disable shadows for other elements. this.disableShadow(ctx); } + }, { + key: 'getDistanceToEdge', /** * Calculate the distance between a point (x3,y3) and a line segment from @@ -32438,8 +32440,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {number} y3 * @private */ - }, { - key: 'getDistanceToEdge', value: function getDistanceToEdge(x1, y1, x2, y2, x3, y3, via) { // x3,y3 is the point var returnValue = 0; @@ -32492,6 +32492,8 @@ return /******/ (function(modules) { // webpackBootstrap return Math.sqrt(dx * dx + dy * dy); } + }, { + key: 'drawArrowHead', /** * @@ -32499,8 +32501,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param position * @param viaNode */ - }, { - key: 'drawArrowHead', value: function drawArrowHead(ctx, position, viaNode, selected, hover) { // set style ctx.strokeStyle = this.getColor(ctx, selected, hover); @@ -32642,7 +32642,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilBezierEdgeBase = __webpack_require__(85); @@ -32694,13 +32694,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "cleanup", /** * remove the support nodes * @returns {boolean} */ - }, { - key: "cleanup", value: function cleanup() { this.body.emitter.off("_repositionBezierNodes", this._boundFunction); if (this.via !== undefined) { @@ -32710,6 +32710,8 @@ return /******/ (function(modules) { // webpackBootstrap } return false; } + }, { + key: "setupSupportNode", /** * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but @@ -32718,14 +32720,12 @@ return /******/ (function(modules) { // webpackBootstrap * The changed data is not called, if needed, it is returned by the main edge constructor. * @private */ - }, { - key: "setupSupportNode", value: function setupSupportNode() { if (this.via === undefined) { var nodeId = "edgeId:" + this.id; var node = this.body.functions.createNode({ id: nodeId, - shape: 'circle', + shape: "circle", physics: true, hidden: true }); @@ -32746,14 +32746,14 @@ return /******/ (function(modules) { // webpackBootstrap this.via.y = 0; } } + }, { + key: "_line", /** * Draw a line between two nodes * @param {CanvasRenderingContext2D} ctx * @private */ - }, { - key: "_line", value: function _line(ctx) { // draw a straight line ctx.beginPath(); @@ -32765,6 +32765,8 @@ return /******/ (function(modules) { // webpackBootstrap this.disableShadow(ctx); return this.via; } + }, { + key: "getPoint", /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way @@ -32773,8 +32775,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ - }, { - key: "getPoint", value: function getPoint(percentage) { var t = percentage; var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * this.via.x + Math.pow(t, 2) * this.to.x; @@ -32819,7 +32819,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilBezierEdgeBase = __webpack_require__(85); @@ -32834,14 +32834,14 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(BezierEdgeStatic.prototype), 'constructor', this).call(this, options, body, labelModule); } - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - _createClass(BezierEdgeStatic, [{ key: '_line', + + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ value: function _line(ctx) { // draw a straight line ctx.beginPath(); @@ -32890,7 +32890,7 @@ return /******/ (function(modules) { // webpackBootstrap yVia = this.from.y + factor * dy; } } - if (type === "discrete") { + if (type === 'discrete') { xVia = dx < factor * dy ? this.from.x : xVia; } } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { @@ -32911,11 +32911,11 @@ return /******/ (function(modules) { // webpackBootstrap yVia = this.from.y + factor * dx; } } - if (type === "discrete") { + if (type === 'discrete') { yVia = dy < factor * dx ? this.from.y : yVia; } } - } else if (type === "straightCross") { + } else if (type === 'straightCross') { if (Math.abs(this.from.x - this.to.x) <= Math.abs(this.from.y - this.to.y)) { // up - down xVia = this.from.x; @@ -33033,6 +33033,8 @@ return /******/ (function(modules) { // webpackBootstrap // x3,y3 is the point return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via); } + }, { + key: 'getPoint', /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way @@ -33041,8 +33043,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ - }, { - key: 'getPoint', value: function getPoint(percentage) { var via = arguments.length <= 1 || arguments[1] === undefined ? this._getViaCoordinates() : arguments[1]; @@ -33078,7 +33078,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _utilEdgeBase = __webpack_require__(86); @@ -33093,14 +33093,14 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(StraightEdge.prototype), 'constructor', this).call(this, options, body, labelModule); } - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - _createClass(StraightEdge, [{ key: '_line', + + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ value: function _line(ctx) { // draw a straight line ctx.beginPath(); @@ -33112,6 +33112,8 @@ return /******/ (function(modules) { // webpackBootstrap this.disableShadow(ctx); return undefined; } + }, { + key: 'getPoint', /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way @@ -33120,8 +33122,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ - }, { - key: 'getPoint', value: function getPoint(percentage) { return { x: (1 - percentage) * this.from.x + percentage * this.to.x, @@ -33213,10 +33213,6 @@ return /******/ (function(modules) { // webpackBootstrap var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver); - var _workerPhysicsWorkerWrapper = __webpack_require__(99); - - var _workerPhysicsWorkerWrapper2 = _interopRequireDefault(_workerPhysicsWorkerWrapper); - var util = __webpack_require__(1); var PhysicsEngine = (function () { @@ -33249,7 +33245,6 @@ return /******/ (function(modules) { // webpackBootstrap this.options = {}; this.defaultOptions = { enabled: true, - useWorker: false, barnesHut: { theta: 0.5, gravitationalConstant: -2000, @@ -33299,7 +33294,6 @@ return /******/ (function(modules) { // webpackBootstrap util.extend(this.options, this.defaultOptions); this.timestep = 0.5; this.layoutFailed = false; - this.draggingNodes = []; this.bindEventListeners(); } @@ -33339,27 +33333,14 @@ return /******/ (function(modules) { // webpackBootstrap _this.stopSimulation(false); _this.body.emitter.off(); }); - // For identifying which nodes to send to worker thread - this.body.emitter.on('dragStart', function (properties) { - _this.draggingNodes = properties.nodes; - }); - this.body.emitter.on('dragEnd', function () { - _this.draggingNodes = []; - }); - this.body.emitter.on('destroy', function () { - if (_this.physicsWorker) { - _this.physicsWorker.terminate(); - _this.physicsWorker = undefined; - } - }); } + }, { + key: 'setOptions', /** * set the physics options * @param options */ - }, { - key: 'setOptions', value: function setOptions(options) { if (options !== undefined) { if (options === false) { @@ -33384,26 +33365,15 @@ return /******/ (function(modules) { // webpackBootstrap this.timestep = this.options.timestep; } } - if (this.options.useWorker) { - this.initPhysicsWorker(); - this.physicsWorker.postMessage({ type: 'options', data: this.options }); - } else { - this.initEmbeddedPhysics(); - } + this.init(); } + }, { + key: 'init', /** * configure the engine. */ - }, { - key: 'initEmbeddedPhysics', - value: function initEmbeddedPhysics() { - if (this.physicsWorker) { - this.options.useWorker = false; - this.physicsWorker.terminate(); - this.physicsWorker = undefined; - this.updatePhysicsData(); - } + value: function init() { var options; if (this.options.solver === 'forceAtlas2Based') { options = this.options.forceAtlas2Based; @@ -33431,75 +33401,11 @@ return /******/ (function(modules) { // webpackBootstrap this.modelOptions = options; } }, { - key: 'initPhysicsWorker', - value: function initPhysicsWorker() { - var _this2 = this; - - if (!this.physicsWorker) { - if (!__webpack_require__.p) { - var parentScript = document.getElementById('visjs'); - if (parentScript) { - var src = parentScript.getAttribute('src'); - __webpack_require__.p = src.substr(0, src.lastIndexOf('/') + 1); - } else { - var scripts = document.getElementsByTagName('script'); - for (var i = 0; i < scripts.length; i++) { - var src = scripts[i].getAttribute('src'); - if (src && src.length >= 6) { - var position = src.length - 6; - var index = src.indexOf('vis.js', position); - if (index === position) { - __webpack_require__.p = src.substr(0, src.lastIndexOf('/') + 1); - break; - } - } - } - } - } - this.physicsWorker = new _workerPhysicsWorkerWrapper2['default'](); - this.physicsWorker.addEventListener('message', function (event) { - _this2.physicsWorkerMessageHandler(event); - }); - this.physicsWorker.onerror = function (event) { - console.error('Falling back to embedded physics engine'); - _this2.initEmbeddedPhysics(); - // throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")"); - }; - } - } - }, { - key: 'physicsWorkerMessageHandler', - value: function physicsWorkerMessageHandler(event) { - var msg = event.data; - switch (msg.type) { - case 'positions': - this.stabilized = msg.data.stabilized; - var positions = msg.data.positions; - // console.log('received positions', positions); - for (var i = 0; i < this.draggingNodes; i++) { - delete positions[this.draggingNodes[i]]; - } - var nodeIds = Object.keys(positions); - for (var i = 0; i < nodeIds.length; i++) { - var nodeId = nodeIds[i]; - var node = this.body.nodes[nodeId]; - // handle case where we get a positions from an old physicsObject - if (node) { - node.x = positions[nodeId].x; - node.y = positions[nodeId].y; - } - } - break; - default: - console.warn('unhandled physics worker message:', msg); - } - } + key: 'initPhysics', /** * initialize the engine */ - }, { - key: 'initPhysics', value: function initPhysics() { if (this.physicsEnabled === true && this.options.enabled === true) { if (this.options.stabilization.enabled === true) { @@ -33515,35 +33421,21 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('fit'); } } + }, { + key: 'startSimulation', /** * Start the simulation */ - }, { - key: 'startSimulation', value: function startSimulation() { if (this.physicsEnabled === true && this.options.enabled === true) { - if (this.physicsWorker) { - for (var i = 0; i < this.draggingNodes.length; i++) { - var nodeId = this.draggingNodes[i]; - var node = this.body.nodes[nodeId]; - this.physicsWorker.postMessage({ - type: 'update', - data: { - id: nodeId, - x: node.x, - y: node.y - } - }); - } - } this.stabilized = false; // when visible, adaptivity is disabled. this.adaptiveTimestep = false; // this sets the width of all nodes initially which could be required for the avoidOverlap - this.body.emitter.emit("_resizeNodes"); + this.body.emitter.emit('_resizeNodes'); if (this.viewFunction === undefined) { this.viewFunction = this.simulationStep.bind(this); this.body.emitter.on('initRedraw', this.viewFunction); @@ -33553,12 +33445,12 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_redraw'); } } + }, { + key: 'stopSimulation', /** * Stop the simulation, force stabilization. */ - }, { - key: 'stopSimulation', value: function stopSimulation() { var emit = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0]; @@ -33574,13 +33466,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'simulationStep', /** * The viewFunction inserts this step into each renderloop. It calls the physics tick and handles the cleanup at stabilized. * */ - }, { - key: 'simulationStep', value: function simulationStep() { // check if the physics have settled var startTime = Date.now(); @@ -33599,34 +33491,34 @@ return /******/ (function(modules) { // webpackBootstrap this.stopSimulation(); } } + }, { + key: '_emitStabilized', /** * trigger the stabilized event. * @private */ - }, { - key: '_emitStabilized', value: function _emitStabilized() { - var _this3 = this; + var _this2 = this; var amountOfIterations = arguments.length <= 0 || arguments[0] === undefined ? this.stabilizationIterations : arguments[0]; if (this.stabilizationIterations > 1 || this.startedStabilization === true) { setTimeout(function () { - _this3.body.emitter.emit('stabilized', { iterations: amountOfIterations }); - _this3.startedStabilization = false; - _this3.stabilizationIterations = 0; + _this2.body.emitter.emit('stabilized', { iterations: amountOfIterations }); + _this2.startedStabilization = false; + _this2.stabilizationIterations = 0; }, 0); } } + }, { + key: 'physicsTick', /** * A single simulation step (or 'tick') in the physics simulation * * @private */ - }, { - key: 'physicsTick', value: function physicsTick() { // this is here to ensure that there is no start event when the network is already stable. if (this.startedStabilization === false) { @@ -33685,13 +33577,8 @@ return /******/ (function(modules) { // webpackBootstrap } else { // case for the static timestep, we reset it to the one in options and take a normal step. this.timestep = this.options.timestep; - if (this.physicsWorker) { - // console.log('asking working to do a physics iteration'); - this.physicsWorker.postMessage({ type: 'calculateForces' }); - } else { - this.calculateForces(); - this.moveNodes(); - } + this.calculateForces(); + this.moveNodes(); } // determine if the network has stabilzied @@ -33702,128 +33589,63 @@ return /******/ (function(modules) { // webpackBootstrap this.stabilizationIterations++; } } + }, { + key: 'updatePhysicsData', /** * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. * * @private */ - }, { - key: 'updatePhysicsData', value: function updatePhysicsData() { + this.physicsBody.forces = {}; + this.physicsBody.physicsNodeIndices = []; + this.physicsBody.physicsEdgeIndices = []; var nodes = this.body.nodes; var edges = this.body.edges; - if (this.physicsWorker) { - var physicsWorkerNodes = {}; - var physicsWorkerEdges = {}; - - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - var node = nodes[nodeId]; - if (node.options.physics === true) { - physicsWorkerNodes[nodeId] = { - id: node.id, - x: node.x, - y: node.y, - options: { - fixed: { - x: node.options.fixed.x, - y: node.options.fixed.y - }, - mass: node.options.mass - } - }; - } - } - } - - for (var edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - var edge = edges[edgeId]; - if (edge.options.physics === true) { - physicsWorkerEdges[edgeId] = { - connected: edge.connected, - id: edge.id, - edgeType: {}, - toId: edge.toId, - fromId: edge.fromId, - to: { - id: edge.to.id - }, - from: { - id: edge.from.id - }, - options: { - length: edge.length - } - }; - if (edge.edgeType.via) { - physicsWorkerEdges[edgeId].edgeType = { - via: { - id: edge.edgeType.via.id - } - }; - } - } - } - } - - this.physicsWorker.postMessage({ - type: 'physicsObjects', - data: { - nodes: physicsWorkerNodes, - edges: physicsWorkerEdges - } - }); - } else { - this.physicsBody.forces = {}; - this.physicsBody.physicsNodeIndices = []; - this.physicsBody.physicsEdgeIndices = []; - - // get node indices for physics - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].options.physics === true) { - this.physicsBody.physicsNodeIndices.push(nodeId); - } + // get node indices for physics + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + if (nodes[nodeId].options.physics === true) { + this.physicsBody.physicsNodeIndices.push(nodeId); } } + } - // get edge indices for physics - for (var edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].options.physics === true) { - this.physicsBody.physicsEdgeIndices.push(edgeId); - } + // get edge indices for physics + for (var edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + if (edges[edgeId].options.physics === true) { + this.physicsBody.physicsEdgeIndices.push(edgeId); } } + } - // get the velocity and the forces vector - for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { - var nodeId = this.physicsBody.physicsNodeIndices[i]; - this.physicsBody.forces[nodeId] = { x: 0, y: 0 }; + // get the velocity and the forces vector + for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { + var nodeId = this.physicsBody.physicsNodeIndices[i]; + this.physicsBody.forces[nodeId] = { x: 0, y: 0 }; - // forces can be reset because they are recalculated. Velocities have to persist. - if (this.physicsBody.velocities[nodeId] === undefined) { - this.physicsBody.velocities[nodeId] = { x: 0, y: 0 }; - } + // forces can be reset because they are recalculated. Velocities have to persist. + if (this.physicsBody.velocities[nodeId] === undefined) { + this.physicsBody.velocities[nodeId] = { x: 0, y: 0 }; } + } - // clean deleted nodes from the velocity vector - for (var nodeId in this.physicsBody.velocities) { - if (nodes[nodeId] === undefined) { - delete this.physicsBody.velocities[nodeId]; - } + // clean deleted nodes from the velocity vector + for (var nodeId in this.physicsBody.velocities) { + if (nodes[nodeId] === undefined) { + delete this.physicsBody.velocities[nodeId]; } } } + }, { + key: 'revert', /** * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. */ - }, { - key: 'revert', value: function revert() { var nodeIds = Object.keys(this.previousStates); var nodes = this.body.nodes; @@ -33847,12 +33669,12 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_evaluateStepQuality', /** * This compares the reference state to the current state */ - }, { - key: '_evaluateStepQuality', value: function _evaluateStepQuality() { var dx = undefined, dy = undefined, @@ -33875,13 +33697,13 @@ return /******/ (function(modules) { // webpackBootstrap } return true; } + }, { + key: 'moveNodes', /** * move the nodes one timestap and check if they are stabilized * @returns {boolean} */ - }, { - key: 'moveNodes', value: function moveNodes() { var nodeIndices = this.physicsBody.physicsNodeIndices; var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; @@ -33903,6 +33725,8 @@ return /******/ (function(modules) { // webpackBootstrap this.adaptiveTimestepEnabled = averageNodeVelocity / nodeIndices.length < velocityAdaptiveThreshold; this.stabilized = maxNodeVelocity < this.options.minVelocity; } + }, { + key: '_performStep', /** * Perform the actual step @@ -33912,8 +33736,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: '_performStep', value: function _performStep(nodeId, maxVelocity) { var node = this.body.nodes[nodeId]; var timestep = this.timestep; @@ -33930,9 +33752,9 @@ return /******/ (function(modules) { // webpackBootstrap velocities[nodeId].x = Math.abs(velocities[nodeId].x) > maxVelocity ? velocities[nodeId].x > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].x; node.x += velocities[nodeId].x * timestep; // position } else { - forces[nodeId].x = 0; - velocities[nodeId].x = 0; - } + forces[nodeId].x = 0; + velocities[nodeId].x = 0; + } if (node.options.fixed.y === false) { var dy = this.modelOptions.damping * velocities[nodeId].y; // damping force @@ -33941,24 +33763,26 @@ return /******/ (function(modules) { // webpackBootstrap velocities[nodeId].y = Math.abs(velocities[nodeId].y) > maxVelocity ? velocities[nodeId].y > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].y; node.y += velocities[nodeId].y * timestep; // position } else { - forces[nodeId].y = 0; - velocities[nodeId].y = 0; - } + forces[nodeId].y = 0; + velocities[nodeId].y = 0; + } var totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x, 2) + Math.pow(velocities[nodeId].y, 2)); return totalVelocity; } + }, { + key: 'calculateForces', /** * calculate the forces for one physics iteration. */ - }, { - key: 'calculateForces', value: function calculateForces() { this.gravitySolver.solve(); this.nodesSolver.solve(); this.edgesSolver.solve(); } + }, { + key: '_freezeNodes', /** * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization @@ -33966,8 +33790,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: '_freezeNodes', value: function _freezeNodes() { var nodes = this.body.nodes; for (var id in nodes) { @@ -33980,14 +33802,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_restoreFrozenNodes', /** * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. * * @private */ - }, { - key: '_restoreFrozenNodes', value: function _restoreFrozenNodes() { var nodes = this.body.nodes; for (var id in nodes) { @@ -34000,15 +33822,15 @@ return /******/ (function(modules) { // webpackBootstrap } this.freezeCache = {}; } + }, { + key: 'stabilize', /** * Find a stable position for all nodes * @private */ - }, { - key: 'stabilize', value: function stabilize() { - var _this4 = this; + var _this3 = this; var iterations = arguments.length <= 0 || arguments[0] === undefined ? this.options.stabilization.iterations : arguments[0]; @@ -34026,7 +33848,7 @@ return /******/ (function(modules) { // webpackBootstrap this.adaptiveTimestep = true && this.options.adaptiveTimestep; // this sets the width of all nodes initially which could be required for the avoidOverlap - this.body.emitter.emit("_resizeNodes"); + this.body.emitter.emit('_resizeNodes'); // stop the render loop this.stopSimulation(); @@ -34045,16 +33867,16 @@ return /******/ (function(modules) { // webpackBootstrap this.stabilizationIterations = 0; setTimeout(function () { - return _this4._stabilizationBatch(); + return _this3._stabilizationBatch(); }, 0); } + }, { + key: '_stabilizationBatch', /** * One batch of stabilization * @private */ - }, { - key: '_stabilizationBatch', value: function _stabilizationBatch() { // this is here to ensure that there is at least one start event. if (this.startedStabilization === false) { @@ -34075,13 +33897,13 @@ return /******/ (function(modules) { // webpackBootstrap this._finalizeStabilization(); } } + }, { + key: '_finalizeStabilization', /** * Wrap up the stabilization, fit and emit the events. * @private */ - }, { - key: '_finalizeStabilization', value: function _finalizeStabilization() { this.body.emitter.emit('_allowRedraw'); if (this.options.stabilization.fit === true) { @@ -34149,6 +33971,8 @@ return /******/ (function(modules) { // webpackBootstrap var x = Math.sin(this.randomSeed++) * 10000; return x - Math.floor(x); } + }, { + key: "solve", /** * This function calculates the forces the nodes apply on eachother based on a gravitational model. @@ -34156,8 +33980,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: "solve", value: function solve() { if (this.options.gravitationalConstant !== 0 && this.physicsBody.physicsNodeIndices.length > 0) { var node = undefined; @@ -34184,6 +34006,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "_getForceContribution", /** * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. @@ -34193,8 +34017,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param node * @private */ - }, { - key: "_getForceContribution", value: function _getForceContribution(parentBranch, node) { // we get no force contribution from an empty region if (parentBranch.childrenCount > 0) { @@ -34229,6 +34051,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "_calculateForces", /** * Calculate the forces based on the distance. @@ -34240,8 +34064,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentBranch * @private */ - }, { - key: "_calculateForces", value: function _calculateForces(distance, dx, dy, node, parentBranch) { if (distance === 0) { distance = 0.1; @@ -34261,6 +34083,8 @@ return /******/ (function(modules) { // webpackBootstrap this.physicsBody.forces[node.id].x += fx; this.physicsBody.forces[node.id].y += fy; } + }, { + key: "_formBarnesHutTree", /** * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. @@ -34269,8 +34093,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param nodeIndices * @private */ - }, { - key: "_formBarnesHutTree", value: function _formBarnesHutTree(nodes, nodeIndices) { var node = undefined; var nodeCount = nodeIndices.length; @@ -34306,9 +34128,9 @@ return /******/ (function(modules) { // webpackBootstrap maxY += 0.5 * sizeDiff; } // xSize > ySize else { - minX += 0.5 * sizeDiff; - maxX -= 0.5 * sizeDiff; - } // xSize < ySize + minX += 0.5 * sizeDiff; + maxX -= 0.5 * sizeDiff; + } // xSize < ySize var minimumTreeSize = 1e-5; var rootSize = Math.max(minimumTreeSize, Math.abs(maxX - minX)); @@ -34346,6 +34168,8 @@ return /******/ (function(modules) { // webpackBootstrap // make global return barnesHutTree; } + }, { + key: "_updateBranchMass", /** * this updates the mass of a branch. this is increased by adding a node. @@ -34354,8 +34178,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param node * @private */ - }, { - key: "_updateBranchMass", value: function _updateBranchMass(parentBranch, node) { var totalMass = parentBranch.mass + node.options.mass; var totalMassInv = 1 / totalMass; @@ -34370,6 +34192,8 @@ return /******/ (function(modules) { // webpackBootstrap var biggestSize = Math.max(Math.max(node.height, node.radius), node.width); parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth; } + }, { + key: "_placeInTree", /** * determine in which branch the node will be placed. @@ -34379,8 +34203,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param skipMassUpdate * @private */ - }, { - key: "_placeInTree", value: function _placeInTree(parentBranch, node, skipMassUpdate) { if (skipMassUpdate != true || skipMassUpdate === undefined) { // update the mass of the branch. @@ -34407,6 +34229,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "_placeInRegion", /** * actually place the node in a region (or branch) @@ -34416,8 +34240,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param region * @private */ - }, { - key: "_placeInRegion", value: function _placeInRegion(parentBranch, node, region) { switch (parentBranch.children[region].childrenCount) { case 0: @@ -34444,6 +34266,8 @@ return /******/ (function(modules) { // webpackBootstrap break; } } + }, { + key: "_splitBranch", /** * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch @@ -34452,8 +34276,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentBranch * @private */ - }, { - key: "_splitBranch", value: function _splitBranch(parentBranch) { // if the branch is shaded with a node, replace the node in the new subset. var containedNode = null; @@ -34474,6 +34296,8 @@ return /******/ (function(modules) { // webpackBootstrap this._placeInTree(parentBranch, containedNode); } } + }, { + key: "_insertRegion", /** * This function subdivides the region into four new segments. @@ -34485,8 +34309,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentRange * @private */ - }, { - key: "_insertRegion", value: function _insertRegion(parentBranch, region) { var minX = undefined, maxX = undefined, @@ -34532,6 +34354,8 @@ return /******/ (function(modules) { // webpackBootstrap childrenCount: 0 }; } + }, { + key: "_debug", //--------------------------- DEBUGGING BELOW ---------------------------// @@ -34542,8 +34366,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param color * @private */ - }, { - key: "_debug", value: function _debug(ctx, color) { if (this.barnesHutTree !== undefined) { @@ -34552,6 +34374,8 @@ return /******/ (function(modules) { // webpackBootstrap this._drawBranch(this.barnesHutTree.root, ctx, color); } } + }, { + key: "_drawBranch", /** * This function is for debugging purposes. It draws the branches recursively. @@ -34561,8 +34385,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param color * @private */ - }, { - key: "_drawBranch", value: function _drawBranch(branch, ctx, color) { if (color === undefined) { color = "#FF0000"; @@ -34638,6 +34460,8 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { this.options = options; } + }, { + key: "solve", /** * Calculate the forces the nodes apply on each other based on a repulsion field. @@ -34645,8 +34469,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: "solve", value: function solve() { var dx, dy, distance, fx, fy, repulsingForce, node1, node2; @@ -34733,6 +34555,8 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { this.options = options; } + }, { + key: "solve", /** * Calculate the forces the nodes apply on each other based on a repulsion field. @@ -34740,8 +34564,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: "solve", value: function solve() { var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j; @@ -34824,20 +34646,19 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { this.options = options; } + }, { + key: "solve", /** * This function calculates the springforces on the nodes, accounting for the support nodes. * * @private */ - }, { - key: "solve", value: function solve() { var edgeLength = undefined, edge = undefined; var edgeIndices = this.physicsBody.physicsEdgeIndices; var edges = this.body.edges; - var nodes = this.body.nodes; var node1 = undefined, node2 = undefined, node3 = undefined; @@ -34850,9 +34671,9 @@ return /******/ (function(modules) { // webpackBootstrap if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) { if (edge.edgeType.via !== undefined) { edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; - node1 = nodes[edge.to.id]; - node2 = nodes[edge.edgeType.via.id]; - node3 = nodes[edge.from.id]; + node1 = edge.to; + node2 = edge.edgeType.via; + node3 = edge.from; this._calculateSpringForce(node1, node2, 0.5 * edgeLength); this._calculateSpringForce(node2, node3, 0.5 * edgeLength); @@ -34860,12 +34681,14 @@ return /******/ (function(modules) { // webpackBootstrap // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger. edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5 : edge.options.length; - this._calculateSpringForce(nodes[edge.from.id], nodes[edge.to.id], edgeLength); + this._calculateSpringForce(edge.from, edge.to, edgeLength); } } } } } + }, { + key: "_calculateSpringForce", /** * This is the code actually performing the calculation for the function above. @@ -34875,8 +34698,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param edgeLength * @private */ - }, { - key: "_calculateSpringForce", value: function _calculateSpringForce(node1, node2, edgeLength) { var dx = node1.x - node2.x; var dy = node1.y - node2.y; @@ -34935,14 +34756,14 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { this.options = options; } + }, { + key: "solve", /** * This function calculates the springforces on the nodes, accounting for the support nodes. * * @private */ - }, { - key: "solve", value: function solve() { var edgeLength, edge; var dx, dy, fx, fy, springForce, distance; @@ -35085,13 +34906,13 @@ return /******/ (function(modules) { // webpackBootstrap this._calculateForces(distance, dx, dy, forces, node); } } + }, { + key: "_calculateForces", /** * Calculate the forces based on the distance. * @private */ - }, { - key: "_calculateForces", value: function _calculateForces(distance, dx, dy, forces, node) { var gravityForce = distance === 0 ? 0 : this.options.centralGravity / distance; forces[node.id].x = dx * gravityForce; @@ -35123,7 +34944,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _BarnesHutSolver2 = __webpack_require__(91); @@ -35138,19 +34959,19 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); } - /** - * Calculate the forces based on the distance. - * - * @param distance - * @param dx - * @param dy - * @param node - * @param parentBranch - * @private - */ - _createClass(ForceAtlas2BasedRepulsionSolver, [{ key: "_calculateForces", + + /** + * Calculate the forces based on the distance. + * + * @param distance + * @param dx + * @param dy + * @param node + * @param parentBranch + * @private + */ value: function _calculateForces(distance, dx, dy, node, parentBranch) { if (distance === 0) { distance = 0.1 * Math.random(); @@ -35197,7 +35018,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _CentralGravitySolver2 = __webpack_require__(96); @@ -35212,13 +35033,13 @@ return /******/ (function(modules) { // webpackBootstrap _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); } - /** - * Calculate the forces based on the distance. - * @private - */ - _createClass(ForceAtlas2BasedCentralGravitySolver, [{ key: "_calculateForces", + + /** + * Calculate the forces based on the distance. + * @private + */ value: function _calculateForces(distance, dx, dy, forces, node) { if (distance > 0) { var degree = node.edges.length + 1; @@ -35237,14 +35058,6 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, /* 99 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = function() { - return new Worker(__webpack_require__.p + "vis.physics.worker.js"); - }; - -/***/ }, -/* 100 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -35259,7 +35072,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsNodesCluster = __webpack_require__(101); + var _componentsNodesCluster = __webpack_require__(100); var _componentsNodesCluster2 = _interopRequireDefault(_componentsNodesCluster); @@ -35288,18 +35101,18 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { if (options !== undefined) {} } + }, { + key: 'clusterByHubsize', /** * * @param hubsize * @param options */ - }, { - key: 'clusterByHubsize', value: function clusterByHubsize(hubsize, options) { if (hubsize === undefined) { hubsize = this._getHubSize(); - } else if (typeof hubsize === "object") { + } else if (typeof hubsize === 'object') { options = this._checkOptions(hubsize); hubsize = this._getHubSize(); } @@ -35318,20 +35131,20 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_dataChanged'); } + }, { + key: 'cluster', /** * loop over all nodes, check if they adhere to the condition and cluster if needed. * @param options * @param refreshData */ - }, { - key: 'cluster', value: function cluster() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var refreshData = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; if (options.joinCondition === undefined) { - throw new Error("Cannot call clusterByNodeData without a joinCondition function in the options."); + throw new Error('Cannot call clusterByNodeData without a joinCondition function in the options.'); } // check if the options object is fine, append if needed @@ -35360,6 +35173,8 @@ return /******/ (function(modules) { // webpackBootstrap this._cluster(childNodesObj, childEdgesObj, options, refreshData); } + }, { + key: 'clusterByEdgeCount', /** * Cluster all nodes in the network that have only X edges @@ -35367,8 +35182,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param options * @param refreshData */ - }, { - key: 'clusterByEdgeCount', value: function clusterByEdgeCount(edgeCount, options) { var refreshData = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2]; @@ -35443,32 +35256,34 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_dataChanged'); } } + }, { + key: 'clusterOutliers', /** * Cluster all nodes in the network that have only 1 edge * @param options * @param refreshData */ - }, { - key: 'clusterOutliers', value: function clusterOutliers(options) { var refreshData = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; this.clusterByEdgeCount(1, options, refreshData); } + }, { + key: 'clusterBridges', /** * Cluster all nodes in the network that have only 2 edge * @param options * @param refreshData */ - }, { - key: 'clusterBridges', value: function clusterBridges(options) { var refreshData = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; this.clusterByEdgeCount(2, options, refreshData); } + }, { + key: 'clusterByConnection', /** * suck all connected nodes of a node into the node. @@ -35476,17 +35291,15 @@ return /******/ (function(modules) { // webpackBootstrap * @param options * @param refreshData */ - }, { - key: 'clusterByConnection', value: function clusterByConnection(nodeId, options) { var refreshData = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2]; // kill conditions if (nodeId === undefined) { - throw new Error("No nodeId supplied to clusterByConnection!"); + throw new Error('No nodeId supplied to clusterByConnection!'); } if (this.body.nodes[nodeId] === undefined) { - throw new Error("The nodeId given to clusterByConnection does not exist!"); + throw new Error('The nodeId given to clusterByConnection does not exist!'); } var node = this.body.nodes[nodeId]; @@ -35539,6 +35352,8 @@ return /******/ (function(modules) { // webpackBootstrap this._cluster(childNodesObj, childEdgesObj, options, refreshData); } + }, { + key: '_cloneOptions', /** * This returns a clone of the options or options of the edge or node to be used for construction of new edges or check functions for new nodes. @@ -35547,8 +35362,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{}} * @private */ - }, { - key: '_cloneOptions', value: function _cloneOptions(item, type) { var clonedOptions = {}; if (type === undefined || type === 'node') { @@ -35561,6 +35374,8 @@ return /******/ (function(modules) { // webpackBootstrap } return clonedOptions; } + }, { + key: '_createClusterEdges', /** * This function creates the edges that will be attached to the cluster @@ -35571,8 +35386,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param options * @private */ - }, { - key: '_createClusterEdges', value: function _createClusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, clusterEdgeProperties) { var edge = undefined, childNodeId = undefined, @@ -35647,6 +35460,8 @@ return /******/ (function(modules) { // webpackBootstrap _edge.hiddenByCluster = true; } } + }, { + key: '_checkOptions', /** * This function checks the options that can be supplied to the different cluster functions @@ -35655,8 +35470,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ - }, { - key: '_checkOptions', value: function _checkOptions() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; @@ -35669,6 +35482,8 @@ return /******/ (function(modules) { // webpackBootstrap return options; } + }, { + key: '_cluster', /** * @@ -35678,8 +35493,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Boolean} refreshData | when true, do not wrap up * @private */ - }, { - key: '_cluster', value: function _cluster(childNodesObj, childEdgesObj, options) { var refreshData = arguments.length <= 3 || arguments[3] === undefined ? true : arguments[3]; @@ -35715,7 +35528,7 @@ return /******/ (function(modules) { // webpackBootstrap for (var edgeId in childEdgesObj) { if (childEdgesObj.hasOwnProperty(edgeId)) { // these cluster edges will be removed on creation of the cluster. - if (edgeId.substr(0, 12) !== "clusterEdge:") { + if (edgeId.substr(0, 12) !== 'clusterEdge:') { var clonedOptions = this._cloneOptions(childEdgesObj[edgeId], 'edge'); childEdgesOptions.push(clonedOptions); } @@ -35724,7 +35537,7 @@ return /******/ (function(modules) { // webpackBootstrap clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions); if (!clusterNodeProperties) { - throw new Error("The processProperties function does not return properties!"); + throw new Error('The processProperties function does not return properties!'); } } @@ -35795,22 +35608,24 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_dataChanged'); } } + }, { + key: 'isCluster', /** * Check if a node is a cluster. * @param nodeId * @returns {*} */ - }, { - key: 'isCluster', value: function isCluster(nodeId) { if (this.body.nodes[nodeId] !== undefined) { return this.body.nodes[nodeId].isCluster === true; } else { - console.log("Node does not exist."); + console.log('Node does not exist.'); return false; } } + }, { + key: '_getClusterPosition', /** * get the position of the cluster node based on what's inside @@ -35818,8 +35633,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @private */ - }, { - key: '_getClusterPosition', value: function _getClusterPosition(childNodesObj) { var childKeys = Object.keys(childNodesObj); var minX = childNodesObj[childKeys[0]].x; @@ -35837,26 +35650,26 @@ return /******/ (function(modules) { // webpackBootstrap return { x: 0.5 * (minX + maxX), y: 0.5 * (minY + maxY) }; } + }, { + key: 'openCluster', /** * Open a cluster by calling this function. * @param {String} clusterNodeId | the ID of the cluster node * @param {Boolean} refreshData | wrap up afterwards if not true */ - }, { - key: 'openCluster', value: function openCluster(clusterNodeId, options) { var refreshData = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2]; // kill conditions if (clusterNodeId === undefined) { - throw new Error("No clusterNodeId supplied to openCluster."); + throw new Error('No clusterNodeId supplied to openCluster.'); } if (this.body.nodes[clusterNodeId] === undefined) { - throw new Error("The clusterNodeId supplied to openCluster does not exist."); + throw new Error('The clusterNodeId supplied to openCluster does not exist.'); } if (this.body.nodes[clusterNodeId].containedNodes === undefined) { - console.log("The node:" + clusterNodeId + " is not a cluster."); + console.log('The node:' + clusterNodeId + ' is not a cluster.'); return; } var clusterNode = this.body.nodes[clusterNodeId]; @@ -36004,6 +35817,8 @@ return /******/ (function(modules) { // webpackBootstrap return nodesArray; } + }, { + key: 'findNode', /** * Get the stack clusterId's that a certain node resides in. cluster A -> cluster B -> cluster C -> node @@ -36011,8 +35826,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {Array} * @private */ - }, { - key: 'findNode', value: function findNode(nodeId) { var stack = []; var max = 100; @@ -36026,6 +35839,8 @@ return /******/ (function(modules) { // webpackBootstrap stack.push(this.body.nodes[nodeId]); return stack; } + }, { + key: '_getConnectedId', /** * Get the Id the node is connected to @@ -36034,8 +35849,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ - }, { - key: '_getConnectedId', value: function _getConnectedId(edge, nodeId) { if (edge.toId != nodeId) { return edge.toId; @@ -36045,6 +35858,8 @@ return /******/ (function(modules) { // webpackBootstrap return edge.fromId; } } + }, { + key: '_getHubSize', /** * We determine how many connections denote an important hub. @@ -36052,8 +35867,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: '_getHubSize', value: function _getHubSize() { var average = 0; var averageSquared = 0; @@ -36093,7 +35906,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 101 */ +/* 100 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -36108,7 +35921,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var _Node2 = __webpack_require__(62); @@ -36138,7 +35951,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 102 */ +/* 101 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -36189,33 +36002,33 @@ return /******/ (function(modules) { // webpackBootstrap value: function bindEventListeners() { var _this = this; - this.body.emitter.on("dragStart", function () { + this.body.emitter.on('dragStart', function () { _this.dragging = true; }); - this.body.emitter.on("dragEnd", function () { + this.body.emitter.on('dragEnd', function () { return _this.dragging = false; }); - this.body.emitter.on("_resizeNodes", function () { + this.body.emitter.on('_resizeNodes', function () { return _this._resizeNodes(); }); - this.body.emitter.on("_redraw", function () { + this.body.emitter.on('_redraw', function () { if (_this.renderingActive === false) { _this._redraw(); } }); - this.body.emitter.on("_blockRedraw", function () { + this.body.emitter.on('_blockRedraw', function () { _this.allowRedraw = false; }); - this.body.emitter.on("_allowRedraw", function () { + this.body.emitter.on('_allowRedraw', function () { _this.allowRedraw = true;_this.redrawRequested = false; }); - this.body.emitter.on("_requestRedraw", this._requestRedraw.bind(this)); - this.body.emitter.on("_startRendering", function () { + this.body.emitter.on('_requestRedraw', this._requestRedraw.bind(this)); + this.body.emitter.on('_startRendering', function () { _this.renderRequests += 1; _this.renderingActive = true; _this._startRendering(); }); - this.body.emitter.on("_stopRendering", function () { + this.body.emitter.on('_stopRendering', function () { _this.renderRequests -= 1; _this.renderingActive = _this.renderRequests > 0; _this.renderTimer = undefined; @@ -36248,8 +36061,8 @@ return /******/ (function(modules) { // webpackBootstrap if (this.requiresTimeout === true) { this.renderTimer = window.setTimeout(this._renderStep.bind(this), this.simulationInterval); // wait this.renderTimeStep milliseconds and perform the animation step function } else { - this.renderTimer = window.requestAnimationFrame(this._renderStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function - } + this.renderTimer = window.requestAnimationFrame(this._renderStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function + } } } } @@ -36273,25 +36086,25 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'redraw', /** * Redraw the network with the current data * chart will be resized too. */ - }, { - key: 'redraw', value: function redraw() { this.body.emitter.emit('setSize'); this._redraw(); } + }, { + key: '_requestRedraw', /** * Redraw the network with the current data * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over. * @private */ - }, { - key: '_requestRedraw', value: function _requestRedraw() { var _this2 = this; @@ -36314,7 +36127,7 @@ return /******/ (function(modules) { // webpackBootstrap var hidden = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; if (this.allowRedraw === true) { - this.body.emitter.emit("initRedraw"); + this.body.emitter.emit('initRedraw'); this.redrawRequested = false; var ctx = this.canvas.frame.canvas.getContext('2d'); @@ -36344,7 +36157,7 @@ return /******/ (function(modules) { // webpackBootstrap ctx.scale(this.body.view.scale, this.body.view.scale); ctx.beginPath(); - this.body.emitter.emit("beforeDrawing", ctx); + this.body.emitter.emit('beforeDrawing', ctx); ctx.closePath(); if (hidden === false) { @@ -36363,7 +36176,7 @@ return /******/ (function(modules) { // webpackBootstrap ctx.beginPath(); //this.physics.nodesSolver._debug(ctx,"#F00F0F"); - this.body.emitter.emit("afterDrawing", ctx); + this.body.emitter.emit('afterDrawing', ctx); ctx.closePath(); // restore original scaling and translation ctx.restore(); @@ -36373,6 +36186,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_resizeNodes', /** * Redraw all nodes @@ -36381,8 +36196,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Boolean} [alwaysShow] * @private */ - }, { - key: '_resizeNodes', value: function _resizeNodes() { var ctx = this.canvas.frame.canvas.getContext('2d'); if (this.pixelRatio === undefined) { @@ -36408,6 +36221,8 @@ return /******/ (function(modules) { // webpackBootstrap // restore original scaling and translation ctx.restore(); } + }, { + key: '_drawNodes', /** * Redraw all nodes @@ -36416,8 +36231,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Boolean} [alwaysShow] * @private */ - }, { - key: '_drawNodes', value: function _drawNodes(ctx) { var alwaysShow = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -36456,6 +36269,8 @@ return /******/ (function(modules) { // webpackBootstrap node.draw(ctx); } } + }, { + key: '_drawEdges', /** * Redraw all edges @@ -36463,8 +36278,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {CanvasRenderingContext2D} ctx * @private */ - }, { - key: '_drawEdges', value: function _drawEdges(ctx) { var edges = this.body.edges; var edgeIndices = this.body.edgeIndices; @@ -36477,6 +36290,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_drawControlNodes', /** * Redraw all edges @@ -36484,8 +36299,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {CanvasRenderingContext2D} ctx * @private */ - }, { - key: '_drawControlNodes', value: function _drawControlNodes(ctx) { var edges = this.body.edges; var edgeIndices = this.body.edgeIndices; @@ -36496,14 +36309,14 @@ return /******/ (function(modules) { // webpackBootstrap edge._drawControlNodes(ctx); } } + }, { + key: '_determineBrowserMethod', /** * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because * some implementations (safari and IE9) did not support requestAnimationFrame * @private */ - }, { - key: '_determineBrowserMethod', value: function _determineBrowserMethod() { if (typeof window !== 'undefined') { var browserType = navigator.userAgent.toLowerCase(); @@ -36530,7 +36343,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 103 */ +/* 102 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -36583,7 +36396,7 @@ return /******/ (function(modules) { // webpackBootstrap var _this = this; // bind the events - this.body.emitter.once("resize", function (obj) { + this.body.emitter.once('resize', function (obj) { if (obj.width !== 0) { _this.body.view.translation.x = obj.width * 0.5; } @@ -36591,8 +36404,8 @@ return /******/ (function(modules) { // webpackBootstrap _this.body.view.translation.y = obj.height * 0.5; } }); - this.body.emitter.on("setSize", this.setSize.bind(this)); - this.body.emitter.on("destroy", function () { + this.body.emitter.on('setSize', this.setSize.bind(this)); + this.body.emitter.on('destroy', function () { _this.hammerFrame.destroy(); _this.hammer.destroy(); _this._cleanUp(); @@ -36614,7 +36427,7 @@ return /******/ (function(modules) { // webpackBootstrap this.resizeTimer = setInterval(function () { var changed = _this2.setSize(); if (changed === true) { - _this2.body.emitter.emit("_requestRedraw"); + _this2.body.emitter.emit('_requestRedraw'); } }, 1000); this.resizeFunction = this._onResize.bind(this); @@ -36635,15 +36448,15 @@ return /******/ (function(modules) { // webpackBootstrap key: '_onResize', value: function _onResize() { this.setSize(); - this.body.emitter.emit("_redraw"); + this.body.emitter.emit('_redraw'); } + }, { + key: '_getCameraState', /** * Get and store the cameraState * @private */ - }, { - key: '_getCameraState', value: function _getCameraState() { var pixelRatio = arguments.length <= 0 || arguments[0] === undefined ? this.pixelRatio : arguments[0]; @@ -36652,13 +36465,13 @@ return /******/ (function(modules) { // webpackBootstrap this.cameraState.scale = this.body.view.scale; this.cameraState.position = this.DOMtoCanvas({ x: 0.5 * this.frame.canvas.width / pixelRatio, y: 0.5 * this.frame.canvas.height / pixelRatio }); } + }, { + key: '_setCameraState', /** * Set the cameraState * @private */ - }, { - key: '_setCameraState', value: function _setCameraState() { if (this.cameraState.scale !== undefined && this.frame.canvas.clientWidth !== 0 && this.frame.canvas.clientHeight !== 0 && this.pixelRatio !== 0 && this.cameraState.previousWidth > 0) { @@ -36692,12 +36505,12 @@ return /******/ (function(modules) { // webpackBootstrap } throw new Error('Could not use the value supplied for width or height:' + value); } + }, { + key: '_create', /** * Create the HTML */ - }, { - key: '_create', value: function _create() { // remove all elements from the container element. while (this.body.container.hasChildNodes()) { @@ -36712,7 +36525,7 @@ return /******/ (function(modules) { // webpackBootstrap ////////////////////////////////////////////////////////////////// - this.frame.canvas = document.createElement("canvas"); + this.frame.canvas = document.createElement('canvas'); this.frame.canvas.style.position = 'relative'; this.frame.appendChild(this.frame.canvas); @@ -36724,10 +36537,10 @@ return /******/ (function(modules) { // webpackBootstrap noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; this.frame.canvas.appendChild(noCanvas); } else { - var ctx = this.frame.canvas.getContext("2d"); + var ctx = this.frame.canvas.getContext('2d'); this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); - this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + this.frame.canvas.getContext('2d').setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); } // add the frame to the container element @@ -36738,13 +36551,13 @@ return /******/ (function(modules) { // webpackBootstrap this._bindHammer(); } + }, { + key: '_bindHammer', /** * This function binds hammer, it can be repeated over and over due to the uniqueness check. * @private */ - }, { - key: '_bindHammer', value: function _bindHammer() { var _this3 = this; @@ -36805,6 +36618,8 @@ return /******/ (function(modules) { // webpackBootstrap _this3.body.eventListeners.onRelease(event); }); } + }, { + key: 'setSize', /** * Set a new size for the network @@ -36813,8 +36628,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {string} height Height in pixels or percentage (for example '400px' * or '30%') */ - }, { - key: 'setSize', value: function setSize() { var width = arguments.length <= 0 || arguments[0] === undefined ? this.options.width : arguments[0]; var height = arguments.length <= 1 || arguments[1] === undefined ? this.options.height : arguments[1]; @@ -36827,7 +36640,7 @@ return /******/ (function(modules) { // webpackBootstrap var oldHeight = this.frame.canvas.height; // update the pixelratio - var ctx = this.frame.canvas.getContext("2d"); + var ctx = this.frame.canvas.getContext('2d'); var previousRation = this.pixelRatio; // we cache this because the camera state storage needs the old value this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); @@ -36893,6 +36706,8 @@ return /******/ (function(modules) { // webpackBootstrap value: function _XconvertDOMtoCanvas(x) { return (x - this.body.view.translation.x) / this.body.view.scale; } + }, { + key: '_XconvertCanvasToDOM', /** * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to @@ -36901,11 +36716,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: '_XconvertCanvasToDOM', value: function _XconvertCanvasToDOM(x) { return x * this.body.view.scale + this.body.view.translation.x; } + }, { + key: '_YconvertDOMtoCanvas', /** * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to @@ -36914,11 +36729,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: '_YconvertDOMtoCanvas', value: function _YconvertDOMtoCanvas(y) { return (y - this.body.view.translation.y) / this.body.view.scale; } + }, { + key: '_YconvertCanvasToDOM', /** * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to @@ -36927,11 +36742,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: '_YconvertCanvasToDOM', value: function _YconvertCanvasToDOM(y) { return y * this.body.view.scale + this.body.view.translation.y; } + }, { + key: 'canvasToDOM', /** * @@ -36939,11 +36754,11 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @constructor */ - }, { - key: 'canvasToDOM', value: function canvasToDOM(pos) { return { x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y) }; } + }, { + key: 'DOMtoCanvas', /** * @@ -36951,8 +36766,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{x: number, y: number}} * @constructor */ - }, { - key: 'DOMtoCanvas', value: function DOMtoCanvas(pos) { return { x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y) }; } @@ -36965,7 +36778,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 104 */ +/* 103 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -36980,7 +36793,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _NetworkUtil = __webpack_require__(105); + var _NetworkUtil = __webpack_require__(104); var _NetworkUtil2 = _interopRequireDefault(_NetworkUtil); @@ -36996,7 +36809,7 @@ return /******/ (function(modules) { // webpackBootstrap this.canvas = canvas; this.animationSpeed = 1 / this.renderRefreshRate; - this.animationEasingFunction = "easeInOutQuint"; + this.animationEasingFunction = 'easeInOutQuint'; this.easingTime = 0; this.sourceScale = 0; this.targetScale = 0; @@ -37008,11 +36821,11 @@ return /******/ (function(modules) { // webpackBootstrap this.viewFunction = undefined; - this.body.emitter.on("fit", this.fit.bind(this)); - this.body.emitter.on("animationFinished", function () { - _this.body.emitter.emit("_stopRendering"); + this.body.emitter.on('fit', this.fit.bind(this)); + this.body.emitter.on('animationFinished', function () { + _this.body.emitter.emit('_stopRendering'); }); - this.body.emitter.on("unlockNode", this.releaseNode.bind(this)); + this.body.emitter.on('unlockNode', this.releaseNode.bind(this)); } _createClass(View, [{ @@ -37022,14 +36835,14 @@ return /******/ (function(modules) { // webpackBootstrap this.options = options; } + }, { + key: 'fit', /** * This function zooms out to fit all data on screen based on amount of nodes * @param {Object} Options * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; */ - }, { - key: 'fit', value: function fit() { var options = arguments.length <= 0 || arguments[0] === undefined ? { nodes: [] } : arguments[0]; var initialZoom = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; @@ -37065,7 +36878,7 @@ return /******/ (function(modules) { // webpackBootstrap var factor = Math.min(this.canvas.frame.canvas.clientWidth / 600, this.canvas.frame.canvas.clientHeight / 600); zoomLevel *= factor; } else { - this.body.emitter.emit("_resizeNodes"); + this.body.emitter.emit('_resizeNodes'); range = _NetworkUtil2['default']._getRange(this.body.nodes, options.nodes); var xDistance = Math.abs(range.maxX - range.minX) * 1.1; @@ -37087,6 +36900,8 @@ return /******/ (function(modules) { // webpackBootstrap var animationOptions = { position: center, scale: zoomLevel, animation: options.animation }; this.moveTo(animationOptions); } + }, { + key: 'focus', // animation @@ -37096,8 +36911,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Number} nodeId * @param {Number} [options] */ - }, { - key: 'focus', value: function focus(nodeId) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; @@ -37108,9 +36921,11 @@ return /******/ (function(modules) { // webpackBootstrap this.moveTo(options); } else { - console.log("Node: " + nodeId + " cannot be found."); + console.log('Node: ' + nodeId + ' cannot be found.'); } } + }, { + key: 'moveTo', /** * @@ -37119,8 +36934,6 @@ return /******/ (function(modules) { // webpackBootstrap * | options.position = {x:Number, y:Number} // position to move to * | options.animation = {duration:Number, easingFunction:String} || Boolean // position to move to */ - }, { - key: 'moveTo', value: function moveTo(options) { if (options === undefined) { options = {}; @@ -37154,11 +36967,13 @@ return /******/ (function(modules) { // webpackBootstrap options.animation.duration = 1000; } // default duration if (options.animation.easingFunction === undefined) { - options.animation.easingFunction = "easeInOutQuad"; + options.animation.easingFunction = 'easeInOutQuad'; } // default easing function this.animateView(options); } + }, { + key: 'animateView', /** * @@ -37171,8 +36986,6 @@ return /******/ (function(modules) { // webpackBootstrap * // easeInQuart, easeOutQuart, easeInOutQuart, * // easeInQuint, easeOutQuint, easeInOutQuint */ - }, { - key: 'animateView', value: function animateView(options) { if (options === undefined) { return; @@ -37212,28 +37025,28 @@ return /******/ (function(modules) { // webpackBootstrap if (options.animation.duration === 0) { if (this.lockedOnNodeId != undefined) { this.viewFunction = this._lockedRedraw.bind(this); - this.body.emitter.on("initRedraw", this.viewFunction); + this.body.emitter.on('initRedraw', this.viewFunction); } else { this.body.view.scale = this.targetScale; this.body.view.translation = this.targetTranslation; - this.body.emitter.emit("_requestRedraw"); + this.body.emitter.emit('_requestRedraw'); } } else { this.animationSpeed = 1 / (60 * options.animation.duration * 0.001) || 1 / 60; // 60 for 60 seconds, 0.001 for milli's this.animationEasingFunction = options.animation.easingFunction; this.viewFunction = this._transitionRedraw.bind(this); - this.body.emitter.on("initRedraw", this.viewFunction); - this.body.emitter.emit("_startRendering"); + this.body.emitter.on('initRedraw', this.viewFunction); + this.body.emitter.emit('_startRendering'); } } + }, { + key: '_lockedRedraw', /** * used to animate smoothly by hijacking the redraw function. * @private */ - }, { - key: '_lockedRedraw', value: function _lockedRedraw() { var nodePosition = { x: this.body.nodes[this.lockedOnNodeId].x, y: this.body.nodes[this.lockedOnNodeId].y }; var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight }); @@ -37253,19 +37066,19 @@ return /******/ (function(modules) { // webpackBootstrap key: 'releaseNode', value: function releaseNode() { if (this.lockedOnNodeId !== undefined && this.viewFunction !== undefined) { - this.body.emitter.off("initRedraw", this.viewFunction); + this.body.emitter.off('initRedraw', this.viewFunction); this.lockedOnNodeId = undefined; this.lockedOnNodeOffset = undefined; } } + }, { + key: '_transitionRedraw', /** * * @param easingTime * @private */ - }, { - key: '_transitionRedraw', value: function _transitionRedraw() { var finished = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; @@ -37282,13 +37095,13 @@ return /******/ (function(modules) { // webpackBootstrap // cleanup if (this.easingTime >= 1.0) { - this.body.emitter.off("initRedraw", this.viewFunction); + this.body.emitter.off('initRedraw', this.viewFunction); this.easingTime = 0; if (this.lockedOnNodeId != undefined) { this.viewFunction = this._lockedRedraw.bind(this); - this.body.emitter.on("initRedraw", this.viewFunction); + this.body.emitter.on('initRedraw', this.viewFunction); } - this.body.emitter.emit("animationFinished"); + this.body.emitter.emit('animationFinished'); } } }, { @@ -37310,7 +37123,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 105 */ +/* 104 */ /***/ function(module, exports) { "use strict"; @@ -37328,13 +37141,13 @@ return /******/ (function(modules) { // webpackBootstrap _classCallCheck(this, NetworkUtil); } - /** - * Find the center position of the network considering the bounding boxes - * @private - */ - _createClass(NetworkUtil, null, [{ key: "_getRange", + + /** + * Find the center position of the network considering the bounding boxes + * @private + */ value: function _getRange(allNodes) { var specificNodes = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; @@ -37366,13 +37179,13 @@ return /******/ (function(modules) { // webpackBootstrap } return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; } + }, { + key: "_getRangeCore", /** * Find the center position of the network * @private */ - }, { - key: "_getRangeCore", value: function _getRangeCore(allNodes) { var specificNodes = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; @@ -37404,14 +37217,14 @@ return /******/ (function(modules) { // webpackBootstrap } return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; } + }, { + key: "_findCenter", /** * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; * @returns {{x: number, y: number}} * @private */ - }, { - key: "_findCenter", value: function _findCenter(range) { return { x: 0.5 * (range.maxX + range.minX), y: 0.5 * (range.maxY + range.minY) }; @@ -37425,7 +37238,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 106 */ +/* 105 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37440,11 +37253,11 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsNavigationHandler = __webpack_require__(107); + var _componentsNavigationHandler = __webpack_require__(106); var _componentsNavigationHandler2 = _interopRequireDefault(_componentsNavigationHandler); - var _componentsPopup = __webpack_require__(108); + var _componentsPopup = __webpack_require__(107); var _componentsPopup2 = _interopRequireDefault(_componentsPopup); @@ -37532,6 +37345,8 @@ return /******/ (function(modules) { // webpackBootstrap this.navigationHandler.setOptions(this.options); } + }, { + key: 'getPointer', /** * Get the pointer location from a touch location @@ -37539,22 +37354,20 @@ return /******/ (function(modules) { // webpackBootstrap * @return {{x: Number, y: Number}} pointer * @private */ - }, { - key: 'getPointer', value: function getPointer(touch) { return { x: touch.x - util.getAbsoluteLeft(this.canvas.frame.canvas), y: touch.y - util.getAbsoluteTop(this.canvas.frame.canvas) }; } + }, { + key: 'onTouch', /** * On start of a touch gesture, store the pointer * @param event * @private */ - }, { - key: 'onTouch', value: function onTouch(event) { if (new Date().valueOf() - this.touchTime > 50) { this.drag.pointer = this.getPointer(event.center); @@ -37564,13 +37377,13 @@ return /******/ (function(modules) { // webpackBootstrap this.touchTime = new Date().valueOf(); } } + }, { + key: 'onTap', /** * handle tap/click event: select/unselect a node * @private */ - }, { - key: 'onTap', value: function onTap(event) { var pointer = this.getPointer(event.center); var multiselect = this.selectionHandler.options.multiselect && (event.changedPointers[0].ctrlKey || event.changedPointers[0].metaKey); @@ -37578,24 +37391,24 @@ return /******/ (function(modules) { // webpackBootstrap this.checkSelectionChanges(pointer, event, multiselect); this.selectionHandler._generateClickEvent('click', event, pointer); } + }, { + key: 'onDoubleTap', /** * handle doubletap event * @private */ - }, { - key: 'onDoubleTap', value: function onDoubleTap(event) { var pointer = this.getPointer(event.center); this.selectionHandler._generateClickEvent('doubleClick', event, pointer); } + }, { + key: 'onHold', /** * handle long tap event: multi select nodes * @private */ - }, { - key: 'onHold', value: function onHold(event) { var pointer = this.getPointer(event.center); var multiselect = this.selectionHandler.options.multiselect; @@ -37605,14 +37418,14 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionHandler._generateClickEvent('click', event, pointer); this.selectionHandler._generateClickEvent('hold', event, pointer); } + }, { + key: 'onRelease', /** * handle the release of the screen * * @private */ - }, { - key: 'onRelease', value: function onRelease(event) { if (new Date().valueOf() - this.touchTime > 10) { var pointer = this.getPointer(event.center); @@ -37627,14 +37440,14 @@ return /******/ (function(modules) { // webpackBootstrap var pointer = this.getPointer({ x: event.clientX, y: event.clientY }); this.selectionHandler._generateClickEvent('oncontext', event, pointer); } + }, { + key: 'checkSelectionChanges', /** * * @param pointer * @param add */ - }, { - key: 'checkSelectionChanges', value: function checkSelectionChanges(pointer, event) { var add = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; @@ -37695,6 +37508,8 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionHandler._generateClickEvent('select', event, pointer); } } + }, { + key: '_determineIfDifferent', /** * This function checks if the nodes and edges previously selected have changed. @@ -37703,8 +37518,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{nodesChanges: boolean, edgesChanges: boolean}} * @private */ - }, { - key: '_determineIfDifferent', value: function _determineIfDifferent(previousSelection, currentSelection) { var nodesChanges = false; var edgesChanges = false; @@ -37732,6 +37545,8 @@ return /******/ (function(modules) { // webpackBootstrap return { nodesChanges: nodesChanges, edgesChanges: edgesChanges }; } + }, { + key: 'onDragStart', /** * This function is called by onDragStart. @@ -37739,8 +37554,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: 'onDragStart', value: function onDragStart(event) { //in case the touch event was triggered on an external div, do the initial touch now. if (this.drag.pointer === undefined) { @@ -37793,13 +37606,13 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionHandler._generateClickEvent('dragStart', event, this.drag.pointer, undefined, true); } } + }, { + key: 'onDrag', /** * handle drag event * @private */ - }, { - key: 'onDrag', value: function onDrag(event) { var _this2 = this; @@ -37855,13 +37668,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'onDragEnd', /** * handle drag start event * @private */ - }, { - key: 'onDragEnd', value: function onDragEnd(event) { this.drag.dragging = false; var selection = this.drag.selection; @@ -37878,14 +37691,14 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_requestRedraw'); } } + }, { + key: 'onPinch', /** * Handle pinch event * @param event * @private */ - }, { - key: 'onPinch', value: function onPinch(event) { var pointer = this.getPointer(event.center); @@ -37898,6 +37711,8 @@ return /******/ (function(modules) { // webpackBootstrap var scale = this.pinch.scale * event.scale; this.zoom(scale, pointer); } + }, { + key: 'zoom', /** * Zoom the network in or out @@ -37906,8 +37721,6 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number} appliedScale scale is limited within the boundaries * @private */ - }, { - key: 'zoom', value: function zoom(scale, pointer) { if (this.options.zoomView === true) { var scaleOld = this.body.view.scale; @@ -37949,6 +37762,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'onMouseWheel', /** * Event handler for mouse wheel event, used to zoom the timeline @@ -37957,8 +37772,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {MouseEvent} event * @private */ - }, { - key: 'onMouseWheel', value: function onMouseWheel(event) { // retrieve delta var delta = 0; @@ -37995,14 +37808,14 @@ return /******/ (function(modules) { // webpackBootstrap // Prevent default actions caused by mouse wheel. event.preventDefault(); } + }, { + key: 'onMouseMove', /** * Mouse move handler for checking whether the title moves over a node with a title. * @param {Event} event * @private */ - }, { - key: 'onMouseMove', value: function onMouseMove(event) { var _this3 = this; @@ -38053,6 +37866,8 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionHandler.hoverObject(obj); } } + }, { + key: '_checkShowPopup', /** * Check if there is an element on the given position in the network @@ -38062,8 +37877,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {{x:Number, y:Number}} pointer * @private */ - }, { - key: '_checkShowPopup', value: function _checkShowPopup(pointer) { var x = this.canvas._XconvertDOMtoCanvas(pointer.x); var y = this.canvas._YconvertDOMtoCanvas(pointer.y); @@ -38148,6 +37961,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_checkHidePopup', /** * Check if the popup must be hidden, which is the case when the mouse is no @@ -38155,8 +37970,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {{x:Number, y:Number}} pointer * @private */ - }, { - key: '_checkHidePopup', value: function _checkHidePopup(pointer) { var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); @@ -38195,7 +38008,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 107 */ +/* 106 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38228,13 +38041,13 @@ return /******/ (function(modules) { // webpackBootstrap this.touchTime = 0; this.activated = false; - this.body.emitter.on("activate", function () { + this.body.emitter.on('activate', function () { _this.activated = true;_this.configureKeyboardBindings(); }); - this.body.emitter.on("deactivate", function () { + this.body.emitter.on('deactivate', function () { _this.activated = false;_this.configureKeyboardBindings(); }); - this.body.emitter.on("destroy", function () { + this.body.emitter.on('destroy', function () { if (_this.keycharm !== undefined) { _this.keycharm.destroy(); } @@ -38282,6 +38095,8 @@ return /******/ (function(modules) { // webpackBootstrap this.iconsCreated = false; } + }, { + key: 'loadNavigationElements', /** * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation @@ -38291,8 +38106,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: 'loadNavigationElements', value: function loadNavigationElements() { var _this2 = this; @@ -38312,7 +38125,7 @@ return /******/ (function(modules) { // webpackBootstrap this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]); var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); - if (navigationDivActions[i] === "_fit") { + if (navigationDivActions[i] === '_fit') { hammerUtil.onTouch(hammer, this._fit.bind(this)); } else { hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i])); @@ -38336,47 +38149,47 @@ return /******/ (function(modules) { // webpackBootstrap value: function bindToRedraw(action) { if (this.boundFunctions[action] === undefined) { this.boundFunctions[action] = this[action].bind(this); - this.body.emitter.on("initRedraw", this.boundFunctions[action]); - this.body.emitter.emit("_startRendering"); + this.body.emitter.on('initRedraw', this.boundFunctions[action]); + this.body.emitter.emit('_startRendering'); } } }, { key: 'unbindFromRedraw', value: function unbindFromRedraw(action) { if (this.boundFunctions[action] !== undefined) { - this.body.emitter.off("initRedraw", this.boundFunctions[action]); - this.body.emitter.emit("_stopRendering"); + this.body.emitter.off('initRedraw', this.boundFunctions[action]); + this.body.emitter.emit('_stopRendering'); delete this.boundFunctions[action]; } } + }, { + key: '_fit', /** * this stops all movement induced by the navigation buttons * * @private */ - }, { - key: '_fit', value: function _fit() { if (new Date().valueOf() - this.touchTime > 700) { // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?) - this.body.emitter.emit("fit", { duration: 700 }); + this.body.emitter.emit('fit', { duration: 700 }); this.touchTime = new Date().valueOf(); } } + }, { + key: '_stopMovement', /** * this stops all movement induced by the navigation buttons * * @private */ - }, { - key: '_stopMovement', value: function _stopMovement() { for (var boundAction in this.boundFunctions) { if (this.boundFunctions.hasOwnProperty(boundAction)) { - this.body.emitter.off("initRedraw", this.boundFunctions[boundAction]); - this.body.emitter.emit("_stopRendering"); + this.body.emitter.off('initRedraw', this.boundFunctions[boundAction]); + this.body.emitter.emit('_stopRendering'); } } this.boundFunctions = {}; @@ -38413,12 +38226,12 @@ return /******/ (function(modules) { // webpackBootstrap this.body.view.scale /= 1 + this.options.keyboard.speed.zoom; this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale }); } + }, { + key: 'configureKeyboardBindings', /** * bind all keys using keycharm. */ - }, { - key: 'configureKeyboardBindings', value: function configureKeyboardBindings() { var _this3 = this; @@ -38436,79 +38249,79 @@ return /******/ (function(modules) { // webpackBootstrap this.keycharm.reset(); if (this.activated === true) { - this.keycharm.bind("up", function () { - _this3.bindToRedraw("_moveUp"); - }, "keydown"); - this.keycharm.bind("down", function () { - _this3.bindToRedraw("_moveDown"); - }, "keydown"); - this.keycharm.bind("left", function () { - _this3.bindToRedraw("_moveLeft"); - }, "keydown"); - this.keycharm.bind("right", function () { - _this3.bindToRedraw("_moveRight"); - }, "keydown"); - this.keycharm.bind("=", function () { - _this3.bindToRedraw("_zoomIn"); - }, "keydown"); - this.keycharm.bind("num+", function () { - _this3.bindToRedraw("_zoomIn"); - }, "keydown"); - this.keycharm.bind("num-", function () { - _this3.bindToRedraw("_zoomOut"); - }, "keydown"); - this.keycharm.bind("-", function () { - _this3.bindToRedraw("_zoomOut"); - }, "keydown"); - this.keycharm.bind("[", function () { - _this3.bindToRedraw("_zoomOut"); - }, "keydown"); - this.keycharm.bind("]", function () { - _this3.bindToRedraw("_zoomIn"); - }, "keydown"); - this.keycharm.bind("pageup", function () { - _this3.bindToRedraw("_zoomIn"); - }, "keydown"); - this.keycharm.bind("pagedown", function () { - _this3.bindToRedraw("_zoomOut"); - }, "keydown"); - - this.keycharm.bind("up", function () { - _this3.unbindFromRedraw("_moveUp"); - }, "keyup"); - this.keycharm.bind("down", function () { - _this3.unbindFromRedraw("_moveDown"); - }, "keyup"); - this.keycharm.bind("left", function () { - _this3.unbindFromRedraw("_moveLeft"); - }, "keyup"); - this.keycharm.bind("right", function () { - _this3.unbindFromRedraw("_moveRight"); - }, "keyup"); - this.keycharm.bind("=", function () { - _this3.unbindFromRedraw("_zoomIn"); - }, "keyup"); - this.keycharm.bind("num+", function () { - _this3.unbindFromRedraw("_zoomIn"); - }, "keyup"); - this.keycharm.bind("num-", function () { - _this3.unbindFromRedraw("_zoomOut"); - }, "keyup"); - this.keycharm.bind("-", function () { - _this3.unbindFromRedraw("_zoomOut"); - }, "keyup"); - this.keycharm.bind("[", function () { - _this3.unbindFromRedraw("_zoomOut"); - }, "keyup"); - this.keycharm.bind("]", function () { - _this3.unbindFromRedraw("_zoomIn"); - }, "keyup"); - this.keycharm.bind("pageup", function () { - _this3.unbindFromRedraw("_zoomIn"); - }, "keyup"); - this.keycharm.bind("pagedown", function () { - _this3.unbindFromRedraw("_zoomOut"); - }, "keyup"); + this.keycharm.bind('up', function () { + _this3.bindToRedraw('_moveUp'); + }, 'keydown'); + this.keycharm.bind('down', function () { + _this3.bindToRedraw('_moveDown'); + }, 'keydown'); + this.keycharm.bind('left', function () { + _this3.bindToRedraw('_moveLeft'); + }, 'keydown'); + this.keycharm.bind('right', function () { + _this3.bindToRedraw('_moveRight'); + }, 'keydown'); + this.keycharm.bind('=', function () { + _this3.bindToRedraw('_zoomIn'); + }, 'keydown'); + this.keycharm.bind('num+', function () { + _this3.bindToRedraw('_zoomIn'); + }, 'keydown'); + this.keycharm.bind('num-', function () { + _this3.bindToRedraw('_zoomOut'); + }, 'keydown'); + this.keycharm.bind('-', function () { + _this3.bindToRedraw('_zoomOut'); + }, 'keydown'); + this.keycharm.bind('[', function () { + _this3.bindToRedraw('_zoomOut'); + }, 'keydown'); + this.keycharm.bind(']', function () { + _this3.bindToRedraw('_zoomIn'); + }, 'keydown'); + this.keycharm.bind('pageup', function () { + _this3.bindToRedraw('_zoomIn'); + }, 'keydown'); + this.keycharm.bind('pagedown', function () { + _this3.bindToRedraw('_zoomOut'); + }, 'keydown'); + + this.keycharm.bind('up', function () { + _this3.unbindFromRedraw('_moveUp'); + }, 'keyup'); + this.keycharm.bind('down', function () { + _this3.unbindFromRedraw('_moveDown'); + }, 'keyup'); + this.keycharm.bind('left', function () { + _this3.unbindFromRedraw('_moveLeft'); + }, 'keyup'); + this.keycharm.bind('right', function () { + _this3.unbindFromRedraw('_moveRight'); + }, 'keyup'); + this.keycharm.bind('=', function () { + _this3.unbindFromRedraw('_zoomIn'); + }, 'keyup'); + this.keycharm.bind('num+', function () { + _this3.unbindFromRedraw('_zoomIn'); + }, 'keyup'); + this.keycharm.bind('num-', function () { + _this3.unbindFromRedraw('_zoomOut'); + }, 'keyup'); + this.keycharm.bind('-', function () { + _this3.unbindFromRedraw('_zoomOut'); + }, 'keyup'); + this.keycharm.bind('[', function () { + _this3.unbindFromRedraw('_zoomOut'); + }, 'keyup'); + this.keycharm.bind(']', function () { + _this3.unbindFromRedraw('_zoomIn'); + }, 'keyup'); + this.keycharm.bind('pageup', function () { + _this3.unbindFromRedraw('_zoomIn'); + }, 'keyup'); + this.keycharm.bind('pagedown', function () { + _this3.unbindFromRedraw('_zoomOut'); + }, 'keyup'); } } } @@ -38521,7 +38334,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 108 */ +/* 107 */ /***/ function(module, exports) { /** @@ -38560,24 +38373,24 @@ return /******/ (function(modules) { // webpackBootstrap this.container.appendChild(this.frame); } - /** - * @param {number} x Horizontal position of the popup window - * @param {number} y Vertical position of the popup window - */ - _createClass(Popup, [{ key: 'setPosition', + + /** + * @param {number} x Horizontal position of the popup window + * @param {number} y Vertical position of the popup window + */ value: function setPosition(x, y) { this.x = parseInt(x); this.y = parseInt(y); } + }, { + key: 'setText', /** * Set the content for the popup window. This can be HTML code or text. * @param {string | Element} content */ - }, { - key: 'setText', value: function setText(content) { if (content instanceof Element) { this.frame.innerHTML = ''; @@ -38586,13 +38399,13 @@ return /******/ (function(modules) { // webpackBootstrap this.frame.innerHTML = content; // string containing text or HTML } } + }, { + key: 'show', /** * Show the popup window * @param {boolean} [doShow] Show or hide the window */ - }, { - key: 'show', value: function show(doShow) { if (doShow === undefined) { doShow = true; @@ -38620,23 +38433,23 @@ return /******/ (function(modules) { // webpackBootstrap left = this.padding; } - this.frame.style.left = left + "px"; - this.frame.style.top = top + "px"; - this.frame.style.visibility = "visible"; + this.frame.style.left = left + 'px'; + this.frame.style.top = top + 'px'; + this.frame.style.visibility = 'visible'; this.hidden = false; } else { this.hide(); } } + }, { + key: 'hide', /** * Hide the popup window */ - }, { - key: 'hide', value: function hide() { this.hidden = true; - this.frame.style.visibility = "hidden"; + this.frame.style.visibility = 'hidden'; } }]); @@ -38647,7 +38460,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 109 */ +/* 108 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -38693,10 +38506,12 @@ return /******/ (function(modules) { // webpackBootstrap key: "setOptions", value: function setOptions(options) { if (options !== undefined) { - var fields = ['multiselect', 'hoverConnectedEdges', 'selectable', 'selectConnectedEdges']; + var fields = ["multiselect", "hoverConnectedEdges", "selectable", "selectConnectedEdges"]; util.selectiveDeepExtend(fields, this.options, options); } } + }, { + key: "selectOnPoint", /** * handles the selection part of the tap; @@ -38704,8 +38519,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Object} pointer * @private */ - }, { - key: "selectOnPoint", value: function selectOnPoint(pointer) { var selected = false; if (this.options.selectable === true) { @@ -38752,14 +38565,14 @@ return /******/ (function(modules) { // webpackBootstrap } else { properties = this.getSelection(); } - properties['pointer'] = { + properties["pointer"] = { DOM: { x: pointer.x, y: pointer.y }, canvas: this.canvas.DOMtoCanvas(pointer) }; - properties['event'] = event; + properties["event"] = event; if (oldSelection !== undefined) { - properties['previousSelection'] = oldSelection; + properties["previousSelection"] = oldSelection; } this.body.emitter.emit(eventType, properties); } @@ -38788,6 +38601,8 @@ return /******/ (function(modules) { // webpackBootstrap this._removeFromSelection(obj); } } + }, { + key: "_getAllNodesOverlappingWith", /** * retrieve all nodes overlapping with given object @@ -38795,8 +38610,6 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number[]} An array with id's of the overlapping nodes * @private */ - }, { - key: "_getAllNodesOverlappingWith", value: function _getAllNodesOverlappingWith(object) { var overlappingNodes = []; var nodes = this.body.nodes; @@ -38808,6 +38621,8 @@ return /******/ (function(modules) { // webpackBootstrap } return overlappingNodes; } + }, { + key: "_pointerToPositionObject", /** * Return a position object in canvasspace from a single point in screenspace @@ -38816,8 +38631,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {{left: number, top: number, right: number, bottom: number}} * @private */ - }, { - key: "_pointerToPositionObject", value: function _pointerToPositionObject(pointer) { var canvasPos = this.canvas.DOMtoCanvas(pointer); return { @@ -38827,6 +38640,8 @@ return /******/ (function(modules) { // webpackBootstrap bottom: canvasPos.y - 1 }; } + }, { + key: "getNodeAt", /** * Get the top node at the a specific point (like a click) @@ -38835,8 +38650,6 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Node | undefined} node * @private */ - }, { - key: "getNodeAt", value: function getNodeAt(pointer) { var returnNode = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; @@ -38855,6 +38668,8 @@ return /******/ (function(modules) { // webpackBootstrap return undefined; } } + }, { + key: "_getEdgesOverlappingWith", /** * retrieve all edges overlapping with given object, selector is around center @@ -38862,8 +38677,6 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number[]} An array with id's of the overlapping nodes * @private */ - }, { - key: "_getEdgesOverlappingWith", value: function _getEdgesOverlappingWith(object, overlappingEdges) { var edges = this.body.edges; for (var i = 0; i < this.body.edgeIndices.length; i++) { @@ -38873,6 +38686,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "_getAllEdgesOverlappingWith", /** * retrieve all nodes overlapping with given object @@ -38880,13 +38695,13 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Number[]} An array with id's of the overlapping nodes * @private */ - }, { - key: "_getAllEdgesOverlappingWith", value: function _getAllEdgesOverlappingWith(object) { var overlappingEdges = []; this._getEdgesOverlappingWith(object, overlappingEdges); return overlappingEdges; } + }, { + key: "getEdgeAt", /** * Place holder. To implement change the getNodeAt to a _getObjectAt. Have the _getObjectAt call @@ -38896,8 +38711,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {undefined} * @private */ - }, { - key: "getEdgeAt", value: function getEdgeAt(pointer) { var returnEdge = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; @@ -38914,6 +38727,8 @@ return /******/ (function(modules) { // webpackBootstrap return undefined; } } + }, { + key: "_addToSelection", /** * Add object to the selection array. @@ -38921,8 +38736,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param obj * @private */ - }, { - key: "_addToSelection", value: function _addToSelection(obj) { if (obj instanceof Node) { this.selectionObj.nodes[obj.id] = obj; @@ -38930,6 +38743,8 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionObj.edges[obj.id] = obj; } } + }, { + key: "_addToHover", /** * Add object to the selection array. @@ -38937,8 +38752,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param obj * @private */ - }, { - key: "_addToHover", value: function _addToHover(obj) { if (obj instanceof Node) { this.hoverObj.nodes[obj.id] = obj; @@ -38946,6 +38759,8 @@ return /******/ (function(modules) { // webpackBootstrap this.hoverObj.edges[obj.id] = obj; } } + }, { + key: "_removeFromSelection", /** * Remove a single option from selection. @@ -38953,8 +38768,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Object} obj * @private */ - }, { - key: "_removeFromSelection", value: function _removeFromSelection(obj) { if (obj instanceof Node) { delete this.selectionObj.nodes[obj.id]; @@ -38962,14 +38775,14 @@ return /******/ (function(modules) { // webpackBootstrap delete this.selectionObj.edges[obj.id]; } } + }, { + key: "unselectAll", /** * Unselect all. The selectionObj is useful for this. * * @private */ - }, { - key: "unselectAll", value: function unselectAll() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -38984,6 +38797,8 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionObj = { nodes: {}, edges: {} }; } + }, { + key: "_getSelectedNodeCount", /** * return the number of selected nodes @@ -38991,8 +38806,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: "_getSelectedNodeCount", value: function _getSelectedNodeCount() { var count = 0; for (var nodeId in this.selectionObj.nodes) { @@ -39002,6 +38815,8 @@ return /******/ (function(modules) { // webpackBootstrap } return count; } + }, { + key: "_getSelectedNode", /** * return the selected node @@ -39009,8 +38824,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: "_getSelectedNode", value: function _getSelectedNode() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -39019,6 +38832,8 @@ return /******/ (function(modules) { // webpackBootstrap } return undefined; } + }, { + key: "_getSelectedEdge", /** * return the selected edge @@ -39026,8 +38841,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: "_getSelectedEdge", value: function _getSelectedEdge() { for (var edgeId in this.selectionObj.edges) { if (this.selectionObj.edges.hasOwnProperty(edgeId)) { @@ -39036,6 +38849,8 @@ return /******/ (function(modules) { // webpackBootstrap } return undefined; } + }, { + key: "_getSelectedEdgeCount", /** * return the number of selected edges @@ -39043,8 +38858,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: "_getSelectedEdgeCount", value: function _getSelectedEdgeCount() { var count = 0; for (var edgeId in this.selectionObj.edges) { @@ -39054,6 +38867,8 @@ return /******/ (function(modules) { // webpackBootstrap } return count; } + }, { + key: "_getSelectedObjectCount", /** * return the number of selected objects. @@ -39061,8 +38876,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: "_getSelectedObjectCount", value: function _getSelectedObjectCount() { var count = 0; for (var nodeId in this.selectionObj.nodes) { @@ -39077,6 +38890,8 @@ return /******/ (function(modules) { // webpackBootstrap } return count; } + }, { + key: "_selectionIsEmpty", /** * Check if anything is selected @@ -39084,8 +38899,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {boolean} * @private */ - }, { - key: "_selectionIsEmpty", value: function _selectionIsEmpty() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -39099,6 +38912,8 @@ return /******/ (function(modules) { // webpackBootstrap } return true; } + }, { + key: "_clusterInSelection", /** * check if one of the selected nodes is a cluster. @@ -39106,8 +38921,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {boolean} * @private */ - }, { - key: "_clusterInSelection", value: function _clusterInSelection() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -39118,6 +38931,8 @@ return /******/ (function(modules) { // webpackBootstrap } return false; } + }, { + key: "_selectConnectedEdges", /** * select the edges connected to the node that is being selected @@ -39125,8 +38940,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node} node * @private */ - }, { - key: "_selectConnectedEdges", value: function _selectConnectedEdges(node) { for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; @@ -39134,6 +38947,8 @@ return /******/ (function(modules) { // webpackBootstrap this._addToSelection(edge); } } + }, { + key: "_hoverConnectedEdges", /** * select the edges connected to the node that is being selected @@ -39141,8 +38956,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node} node * @private */ - }, { - key: "_hoverConnectedEdges", value: function _hoverConnectedEdges(node) { for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; @@ -39150,6 +38963,8 @@ return /******/ (function(modules) { // webpackBootstrap this._addToHover(edge); } } + }, { + key: "_unselectConnectedEdges", /** * unselect the edges connected to the node that is being selected @@ -39157,8 +38972,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node} node * @private */ - }, { - key: "_unselectConnectedEdges", value: function _unselectConnectedEdges(node) { for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; @@ -39166,6 +38979,8 @@ return /******/ (function(modules) { // webpackBootstrap this._removeFromSelection(edge); } } + }, { + key: "blurObject", /** * This is called when someone clicks on a node. either select or deselect it. @@ -39174,8 +38989,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node || Edge} object * @private */ - }, { - key: "blurObject", value: function blurObject(object) { if (object.hover === true) { object.hover = false; @@ -39186,6 +38999,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "hoverObject", /** * This is called when someone clicks on a node. either select or deselect it. @@ -39194,8 +39009,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Node || Edge} object * @private */ - }, { - key: "hoverObject", value: function hoverObject(object) { var hoverChanged = false; // remove all node hover highlights @@ -39220,10 +39033,10 @@ return /******/ (function(modules) { // webpackBootstrap } // if the blur remains the same and the object is undefined (mouse off), we blur the edge else if (object === undefined) { - this.blurObject(this.hoverObj.edges[edgeId]); - delete this.hoverObj.edges[edgeId]; - hoverChanged = true; - } + this.blurObject(this.hoverObj.edges[edgeId]); + delete this.hoverObj.edges[edgeId]; + hoverChanged = true; + } } } @@ -39244,22 +39057,24 @@ return /******/ (function(modules) { // webpackBootstrap } if (hoverChanged === true) { - this.body.emitter.emit('_requestRedraw'); + this.body.emitter.emit("_requestRedraw"); } } + }, { + key: "getSelection", /** * * retrieve the currently selected objects * @return {{nodes: Array., edges: Array.}} selection */ - }, { - key: "getSelection", value: function getSelection() { var nodeIds = this.getSelectedNodes(); var edgeIds = this.getSelectedEdges(); return { nodes: nodeIds, edges: edgeIds }; } + }, { + key: "getSelectedNodes", /** * @@ -39267,8 +39082,6 @@ return /******/ (function(modules) { // webpackBootstrap * @return {String[]} selection An array with the ids of the * selected nodes. */ - }, { - key: "getSelectedNodes", value: function getSelectedNodes() { var idArray = []; if (this.options.selectable === true) { @@ -39280,6 +39093,8 @@ return /******/ (function(modules) { // webpackBootstrap } return idArray; } + }, { + key: "getSelectedEdges", /** * @@ -39287,8 +39102,6 @@ return /******/ (function(modules) { // webpackBootstrap * @return {Array} selection An array with the ids of the * selected nodes. */ - }, { - key: "getSelectedEdges", value: function getSelectedEdges() { var idArray = []; if (this.options.selectable === true) { @@ -39300,6 +39113,8 @@ return /******/ (function(modules) { // webpackBootstrap } return idArray; } + }, { + key: "selectNodes", /** * select zero or more nodes with the option to highlight edges @@ -39307,15 +39122,13 @@ return /******/ (function(modules) { // webpackBootstrap * selected nodes. * @param {boolean} [highlightEdges] */ - }, { - key: "selectNodes", value: function selectNodes(selection) { var highlightEdges = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; var i = undefined, id = undefined; - if (!selection || selection.length === undefined) throw 'Selection must be an array with ids'; + if (!selection || selection.length === undefined) throw "Selection must be an array with ids"; // first unselect any selected node this.unselectAll(); @@ -39325,25 +39138,25 @@ return /******/ (function(modules) { // webpackBootstrap var node = this.body.nodes[id]; if (!node) { - throw new RangeError('Node with id "' + id + '" not found'); + throw new RangeError("Node with id \"" + id + "\" not found"); } this.selectObject(node, highlightEdges); } - this.body.emitter.emit('_requestRedraw'); + this.body.emitter.emit("_requestRedraw"); } + }, { + key: "selectEdges", /** * select zero or more edges * @param {Number[] | String[]} selection An array with the ids of the * selected nodes. */ - }, { - key: "selectEdges", value: function selectEdges(selection) { var i = undefined, id = undefined; - if (!selection || selection.length === undefined) throw 'Selection must be an array with ids'; + if (!selection || selection.length === undefined) throw "Selection must be an array with ids"; // first unselect any selected objects this.unselectAll(); @@ -39353,19 +39166,19 @@ return /******/ (function(modules) { // webpackBootstrap var edge = this.body.edges[id]; if (!edge) { - throw new RangeError('Edge with id "' + id + '" not found'); + throw new RangeError("Edge with id \"" + id + "\" not found"); } this.selectObject(edge); } - this.body.emitter.emit('_requestRedraw'); + this.body.emitter.emit("_requestRedraw"); } + }, { + key: "updateSelection", /** * Validate the selection: remove ids of nodes which no longer exist * @private */ - }, { - key: "updateSelection", value: function updateSelection() { for (var nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { @@ -39391,7 +39204,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 110 */ +/* 109 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39406,7 +39219,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _NetworkUtil = __webpack_require__(105); + var _NetworkUtil = __webpack_require__(104); var _NetworkUtil2 = _interopRequireDefault(_NetworkUtil); @@ -39460,7 +39273,7 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options, allOptions) { if (options !== undefined) { var prevHierarchicalState = this.options.hierarchical.enabled; - util.selectiveDeepExtend(["randomSeed", "improvedLayout"], this.options, options); + util.selectiveDeepExtend(['randomSeed', 'improvedLayout'], this.options, options); util.mergeOptions(this.options, options, 'hierarchical'); if (options.randomSeed !== undefined) { this.initialRandomSeed = options.randomSeed; @@ -39582,13 +39395,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'layoutNetwork', /** * Use KamadaKawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we * cluster them first to reduce the amount. */ - }, { - key: 'layoutNetwork', value: function layoutNetwork() { if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) { // first check if we should KamadaKawai to layout. The threshold is if less than half of the visible @@ -39622,8 +39435,8 @@ return /******/ (function(modules) { // webpackBootstrap var after = this.body.nodeIndices.length; if (before == after && level % 3 !== 0 || level > MAX_LEVELS) { this._declusterAll(); - this.body.emitter.emit("_layoutFailed"); - console.info("This network could not be positioned by this version of the improved layout algorithm. Please disable improvedLayout for better performance."); + this.body.emitter.emit('_layoutFailed'); + console.info('This network could not be positioned by this version of the improved layout algorithm. Please disable improvedLayout for better performance.'); return; } //console.timeEnd("clustering") @@ -39650,17 +39463,17 @@ return /******/ (function(modules) { // webpackBootstrap this._declusterAll(); // reposition all bezier nodes. - this.body.emitter.emit("_repositionBezierNodes"); + this.body.emitter.emit('_repositionBezierNodes'); } } } + }, { + key: '_shiftToCenter', /** * Move all the nodes towards to the center so gravitational pull wil not move the nodes away from view * @private */ - }, { - key: '_shiftToCenter', value: function _shiftToCenter() { var range = _NetworkUtil2['default']._getRangeCore(this.body.nodes, this.body.nodeIndices); var center = _NetworkUtil2['default']._findCenter(range); @@ -39691,6 +39504,8 @@ return /******/ (function(modules) { // webpackBootstrap value: function getSeed() { return this.initialRandomSeed; } + }, { + key: 'setupHierarchicalLayout', /** * This is the main function to layout the nodes in a hierarchical way. @@ -39698,8 +39513,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: 'setupHierarchicalLayout', value: function setupHierarchicalLayout() { if (this.options.hierarchical.enabled === true && this.body.nodeIndices.length > 0) { // get the size of the largest hubs and check if the user has defined a level for a node. @@ -39747,6 +39560,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_placeNodesByHierarchy', /** * This function places the nodes on the canvas based on the hierarchial distribution. @@ -39754,8 +39569,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Object} distribution | obtained by the function this._getDistribution() * @private */ - }, { - key: '_placeNodesByHierarchy', value: function _placeNodesByHierarchy(distribution) { var nodeId = undefined, node = undefined; @@ -39787,6 +39600,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_getDistribution', /** * This function get the distribution of levels based on hubsize @@ -39794,8 +39609,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {Object} * @private */ - }, { - key: '_getDistribution', value: function _getDistribution() { var distribution = {}; var nodeId = undefined, @@ -39823,6 +39636,8 @@ return /******/ (function(modules) { // webpackBootstrap } return distribution; } + }, { + key: '_getHubSize', /** * Get the hubsize from all remaining unlevelled nodes. @@ -39830,8 +39645,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {number} * @private */ - }, { - key: '_getHubSize', value: function _getHubSize() { var hubSize = 0; for (var nodeId in this.body.nodes) { @@ -39844,6 +39657,8 @@ return /******/ (function(modules) { // webpackBootstrap } return hubSize; } + }, { + key: '_determineLevelsByHubsize', /** * this function allocates nodes in levels based on the recursive branching from the largest hubs. @@ -39851,8 +39666,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param hubsize * @private */ - }, { - key: '_determineLevelsByHubsize', value: function _determineLevelsByHubsize() { var nodeId = undefined, node = undefined; @@ -39873,6 +39686,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_setLevelByHubsize', /** * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. @@ -39882,8 +39697,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentId * @private */ - }, { - key: '_setLevelByHubsize', value: function _setLevelByHubsize(level, node) { if (this.hierarchicalLevels[node.id] !== undefined) return; @@ -39898,6 +39711,8 @@ return /******/ (function(modules) { // webpackBootstrap this._setLevelByHubsize(level + 1, childNode); } } + }, { + key: '_determineLevelsDirected', /** * this function allocates nodes in levels based on the direction of the edges @@ -39905,8 +39720,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param hubsize * @private */ - }, { - key: '_determineLevelsDirected', value: function _determineLevelsDirected() { var nodeId = undefined, node = undefined; @@ -39934,6 +39747,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_setLevelDirected', /** * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction @@ -39943,8 +39758,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentId * @private */ - }, { - key: '_setLevelDirected', value: function _setLevelDirected(level, node) { if (this.hierarchicalLevels[node.id] !== undefined) return; @@ -39961,6 +39774,8 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: '_placeBranchNodes', /** * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes @@ -39972,8 +39787,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param parentLevel * @private */ - }, { - key: '_placeBranchNodes', value: function _placeBranchNodes(edges, parentId, distribution, parentLevel) { for (var i = 0; i < edges.length; i++) { var childNode = undefined; @@ -40020,7 +39833,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 111 */ +/* 110 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -40095,13 +39908,13 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.on('_resetData', this._restore.bind(this)); } - /** - * If something changes in the data during editing, switch back to the initial datamanipulation state and close all edit modes. - * @private - */ - _createClass(ManipulationSystem, [{ key: '_restore', + + /** + * If something changes in the data during editing, switch back to the initial datamanipulation state and close all edit modes. + * @private + */ value: function _restore() { if (this.inMode !== false) { if (this.options.initiallyActive === true) { @@ -40111,13 +39924,13 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: 'setOptions', /** * Set the Options * @param options */ - }, { - key: 'setOptions', value: function setOptions(options, allOptions, globalOptions) { if (allOptions !== undefined) { if (allOptions.locale !== undefined) { @@ -40145,14 +39958,14 @@ return /******/ (function(modules) { // webpackBootstrap this._setup(); } } + }, { + key: 'toggleEditMode', /** * Enable or disable edit-mode. Draws the DOM required and cleans up after itself. * * @private */ - }, { - key: 'toggleEditMode', value: function toggleEditMode() { if (this.editMode === true) { this.disableEditMode(); @@ -40186,14 +39999,14 @@ return /******/ (function(modules) { // webpackBootstrap this._createEditButton(); } } + }, { + key: 'showManipulatorToolbar', /** * Creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. * * @private */ - }, { - key: 'showManipulatorToolbar', value: function showManipulatorToolbar() { // restore the state of any bound functions or events, remove control nodes, restore physics this._clean(); @@ -40268,14 +40081,14 @@ return /******/ (function(modules) { // webpackBootstrap // redraw to show any possible changes this.body.emitter.emit('_redraw'); } + }, { + key: 'addNodeMode', /** * Create the toolbar for adding Nodes * * @private */ - }, { - key: 'addNodeMode', value: function addNodeMode() { // when using the gui, enable edit mode if it wasnt already. if (this.editMode !== true) { @@ -40299,14 +40112,14 @@ return /******/ (function(modules) { // webpackBootstrap this._temporaryBindEvent('click', this._performAddNode.bind(this)); } + }, { + key: 'editNode', /** * call the bound function to handle the editing of the node. The node has to be selected. * * @private */ - }, { - key: 'editNode', value: function editNode() { var _this2 = this; @@ -40347,14 +40160,14 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } + }, { + key: 'addEdgeMode', /** * create the toolbar to connect nodes * * @private */ - }, { - key: 'addEdgeMode', value: function addEdgeMode() { // when using the gui, enable edit mode if it wasnt already. if (this.editMode !== true) { @@ -40385,14 +40198,14 @@ return /******/ (function(modules) { // webpackBootstrap this._temporaryBindUI('onDragStart', function () {}); this._temporaryBindUI('onHold', function () {}); } + }, { + key: 'editEdgeMode', /** * create the toolbar to edit edges * * @private */ - }, { - key: 'editEdgeMode', value: function editEdgeMode() { var _this3 = this; @@ -40462,14 +40275,14 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } + }, { + key: 'deleteSelected', /** * delete everything in the selection * * @private */ - }, { - key: 'deleteSelected', value: function deleteSelected() { var _this4 = this; @@ -40527,6 +40340,8 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } + }, { + key: '_setup', //********************************************** PRIVATE ***************************************// @@ -40534,8 +40349,6 @@ return /******/ (function(modules) { // webpackBootstrap * draw or remove the DOM * @private */ - }, { - key: '_setup', value: function _setup() { if (this.options.enabled === true) { // Enable the GUI @@ -40554,13 +40367,13 @@ return /******/ (function(modules) { // webpackBootstrap this.guiEnabled = false; } } + }, { + key: '_createWrappers', /** * create the div overlays that contain the DOM * @private */ - }, { - key: '_createWrappers', value: function _createWrappers() { // load the manipulator HTML elements. All styling done in css. if (this.manipulationDiv === undefined) { @@ -40594,6 +40407,8 @@ return /******/ (function(modules) { // webpackBootstrap this.canvas.frame.appendChild(this.closeDiv); } } + }, { + key: '_getNewTargetNode', /** * generate a new target node. Used for creating new edges and editing edges @@ -40602,8 +40417,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*} * @private */ - }, { - key: '_getNewTargetNode', value: function _getNewTargetNode(x, y) { var controlNodeStyle = util.deepExtend({}, this.options.controlNodeStyle); @@ -40615,12 +40428,12 @@ return /******/ (function(modules) { // webpackBootstrap return this.body.functions.createNode(controlNodeStyle); } + }, { + key: '_createEditButton', /** * Create the edit button */ - }, { - key: '_createEditButton', value: function _createEditButton() { // restore everything to it's original state (if applicable) this._clean(); @@ -40639,13 +40452,13 @@ return /******/ (function(modules) { // webpackBootstrap // bind a hammer listener to the button, calling the function toggleEditMode. this._bindHammerToDiv(button, this.toggleEditMode.bind(this)); } + }, { + key: '_clean', /** * this function cleans up after everything this module does. Temporary elements, functions and events are removed, physics restored, hammers removed. * @private */ - }, { - key: '_clean', value: function _clean() { // not in mode this.inMode = false; @@ -40671,13 +40484,13 @@ return /******/ (function(modules) { // webpackBootstrap // restore the physics if required this.body.emitter.emit('restorePhysics'); } + }, { + key: '_cleanManipulatorHammers', /** * Each dom element has it's own hammer. They are stored in this.manipulationHammers. This cleans them up. * @private */ - }, { - key: '_cleanManipulatorHammers', value: function _cleanManipulatorHammers() { // _clean hammer bindings if (this.manipulationHammers.length != 0) { @@ -40687,13 +40500,13 @@ return /******/ (function(modules) { // webpackBootstrap this.manipulationHammers = []; } } + }, { + key: '_removeManipulationDOM', /** * Remove all DOM elements created by this module. * @private */ - }, { - key: '_removeManipulationDOM', value: function _removeManipulationDOM() { // removes all the bindings and overloads this._clean(); @@ -40719,14 +40532,14 @@ return /******/ (function(modules) { // webpackBootstrap this.editModeDiv = undefined; this.closeDiv = undefined; } + }, { + key: '_createSeperator', /** * create a seperator line. the index is to differentiate in the manipulation dom * @param index * @private */ - }, { - key: '_createSeperator', value: function _createSeperator() { var index = arguments.length <= 0 || arguments[0] === undefined ? 1 : arguments[0]; @@ -40734,11 +40547,11 @@ return /******/ (function(modules) { // webpackBootstrap this.manipulationDOM['seperatorLineDiv' + index].className = 'vis-separator-line'; this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv' + index]); } + }, { + key: '_createAddNodeButton', // ---------------------- DOM functions for buttons --------------------------// - }, { - key: '_createAddNodeButton', value: function _createAddNodeButton(locale) { var button = this._createButton('addNode', 'vis-button vis-add', locale['addNode'] || this.options.locales['en']['addNode']); this.manipulationDiv.appendChild(button); @@ -40797,6 +40610,8 @@ return /******/ (function(modules) { // webpackBootstrap value: function _createDescription(label) { this.manipulationDiv.appendChild(this._createButton('description', 'vis-button vis-none', label)); } + }, { + key: '_temporaryBindEvent', // -------------------------- End of DOM functions for buttons ------------------------------// @@ -40806,12 +40621,12 @@ return /******/ (function(modules) { // webpackBootstrap * @param newFunction * @private */ - }, { - key: '_temporaryBindEvent', value: function _temporaryBindEvent(event, newFunction) { this.temporaryEventFunctions.push({ event: event, boundFunction: newFunction }); this.body.emitter.on(event, newFunction); } + }, { + key: '_temporaryBindUI', /** * this overrides an UI function until cleanup by the clean function @@ -40819,8 +40634,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param newFunction * @private */ - }, { - key: '_temporaryBindUI', value: function _temporaryBindUI(UIfunctionName, newFunction) { if (this.body.eventListeners[UIfunctionName] !== undefined) { this.temporaryUIFunctions[UIfunctionName] = this.body.eventListeners[UIfunctionName]; @@ -40829,14 +40642,14 @@ return /******/ (function(modules) { // webpackBootstrap throw new Error('This UI function does not exist. Typo? You tried: ' + UIfunctionName + ' possible are: ' + JSON.stringify(Object.keys(this.body.eventListeners))); } } + }, { + key: '_unbindTemporaryUIs', /** * Restore the overridden UI functions to their original state. * * @private */ - }, { - key: '_unbindTemporaryUIs', value: function _unbindTemporaryUIs() { for (var functionName in this.temporaryUIFunctions) { if (this.temporaryUIFunctions.hasOwnProperty(functionName)) { @@ -40846,13 +40659,13 @@ return /******/ (function(modules) { // webpackBootstrap } this.temporaryUIFunctions = {}; } + }, { + key: '_unbindTemporaryEvents', /** * Unbind the events created by _temporaryBindEvent * @private */ - }, { - key: '_unbindTemporaryEvents', value: function _unbindTemporaryEvents() { for (var i = 0; i < this.temporaryEventFunctions.length; i++) { var eventName = this.temporaryEventFunctions[i].event; @@ -40861,26 +40674,26 @@ return /******/ (function(modules) { // webpackBootstrap } this.temporaryEventFunctions = []; } + }, { + key: '_bindHammerToDiv', /** * Bind an hammer instance to a DOM element. * @param domElement * @param funct */ - }, { - key: '_bindHammerToDiv', value: function _bindHammerToDiv(domElement, boundFunction) { var hammer = new Hammer(domElement, {}); hammerUtil.onTouch(hammer, boundFunction); this.manipulationHammers.push(hammer); } + }, { + key: '_cleanupTemporaryNodesAndEdges', /** * Neatly clean up temporary edges and nodes * @private */ - }, { - key: '_cleanupTemporaryNodesAndEdges', value: function _cleanupTemporaryNodesAndEdges() { // _clean temporary edges for (var i = 0; i < this.temporaryIds.edges.length; i++) { @@ -40903,6 +40716,8 @@ return /******/ (function(modules) { // webpackBootstrap this.temporaryIds = { nodes: [], edges: [] }; } + }, { + key: '_controlNodeTouch', // ------------------------------------------ EDIT EDGE FUNCTIONS -----------------------------------------// @@ -40911,21 +40726,19 @@ return /******/ (function(modules) { // webpackBootstrap * @param event * @private */ - }, { - key: '_controlNodeTouch', value: function _controlNodeTouch(event) { this.selectionHandler.unselectAll(); this.lastTouch = this.body.functions.getPointer(event.center); this.lastTouch.translation = util.extend({}, this.body.view.translation); // copy the object } + }, { + key: '_controlNodeDragStart', /** * the drag start is used to mark one of the control nodes as selected. * @param event * @private */ - }, { - key: '_controlNodeDragStart', value: function _controlNodeDragStart(event) { var pointer = this.lastTouch; var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); @@ -40947,14 +40760,14 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_redraw'); } + }, { + key: '_controlNodeDrag', /** * dragging the control nodes or the canvas * @param event * @private */ - }, { - key: '_controlNodeDrag', value: function _controlNodeDrag(event) { this.body.emitter.emit('disablePhysics'); var pointer = this.body.functions.getPointer(event.center); @@ -40971,14 +40784,14 @@ return /******/ (function(modules) { // webpackBootstrap } this.body.emitter.emit('_redraw'); } + }, { + key: '_controlNodeDragEnd', /** * connecting or restoring the control nodes. * @param event * @private */ - }, { - key: '_controlNodeDragEnd', value: function _controlNodeDragEnd(event) { var pointer = this.body.functions.getPointer(event.center); var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); @@ -41016,6 +40829,8 @@ return /******/ (function(modules) { // webpackBootstrap } this.body.emitter.emit('_redraw'); } + }, { + key: '_handleConnect', // ------------------------------------ END OF EDIT EDGE FUNCTIONS -----------------------------------------// @@ -41026,8 +40841,6 @@ return /******/ (function(modules) { // webpackBootstrap * * @private */ - }, { - key: '_handleConnect', value: function _handleConnect(event) { // check to avoid double fireing of this function. if (new Date().valueOf() - this.touchTime > 100) { @@ -41083,14 +40896,14 @@ return /******/ (function(modules) { // webpackBootstrap this.body.view.translation = { x: this.lastTouch.translation.x + diffX, y: this.lastTouch.translation.y + diffY }; } } + }, { + key: '_finishConnect', /** * Connect the new edge to the target if one exists, otherwise remove temp line * @param event * @private */ - }, { - key: '_finishConnect', value: function _finishConnect(event) { var pointer = this.body.functions.getPointer(event.center); var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); @@ -41127,6 +40940,8 @@ return /******/ (function(modules) { // webpackBootstrap } this.body.emitter.emit('_redraw'); } + }, { + key: '_performAddNode', // --------------------------------------- END OF ADD EDGE FUNCTIONS -------------------------------------// @@ -41135,8 +40950,6 @@ return /******/ (function(modules) { // webpackBootstrap /** * Adds a node on the specified location */ - }, { - key: '_performAddNode', value: function _performAddNode(clickData) { var _this5 = this; @@ -41165,14 +40978,14 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } + }, { + key: '_performAddEdge', /** * connect two nodes with a new edge. * * @private */ - }, { - key: '_performAddEdge', value: function _performAddEdge(sourceNodeId, targetNodeId) { var _this6 = this; @@ -41196,14 +41009,14 @@ return /******/ (function(modules) { // webpackBootstrap this.showManipulatorToolbar(); } } + }, { + key: '_performEditEdge', /** * connect two nodes with a new edge. * * @private */ - }, { - key: '_performEditEdge', value: function _performEditEdge(sourceNodeId, targetNodeId) { var _this7 = this; @@ -41239,7 +41052,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 112 */ +/* 111 */ /***/ function(module, exports) { /** @@ -41474,7 +41287,6 @@ return /******/ (function(modules) { // webpackBootstrap }, physics: { enabled: { boolean: boolean }, - useWorker: { boolean: boolean }, barnesHut: { gravitationalConstant: { number: number }, centralGravity: { number: number }, @@ -41731,7 +41543,6 @@ return /******/ (function(modules) { // webpackBootstrap solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'], timestep: [0.5, 0.01, 1, 0.01] }, - //adaptiveTimestep: true global: { locale: ['en', 'nl'] } @@ -41739,9 +41550,10 @@ return /******/ (function(modules) { // webpackBootstrap exports.allOptions = allOptions; exports.configureOptions = configureOptions; + //adaptiveTimestep: true /***/ }, -/* 113 */ +/* 112 */ /***/ function(module, exports, __webpack_require__) { /** @@ -41763,7 +41575,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var _componentsAlgorithmsFloydWarshallJs = __webpack_require__(114); + var _componentsAlgorithmsFloydWarshallJs = __webpack_require__(113); var _componentsAlgorithmsFloydWarshallJs2 = _interopRequireDefault(_componentsAlgorithmsFloydWarshallJs); @@ -41786,13 +41598,13 @@ return /******/ (function(modules) { // webpackBootstrap this.distanceSolver = new _componentsAlgorithmsFloydWarshallJs2["default"](); } - /** - * Not sure if needed but can be used to update the spring length and spring constant - * @param options - */ - _createClass(KamadaKawai, [{ key: "setOptions", + + /** + * Not sure if needed but can be used to update the spring length and spring constant + * @param options + */ value: function setOptions(options) { if (options) { if (options.springLength) { @@ -41803,14 +41615,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "solve", /** * Position the system * @param nodesArray * @param edgesArray */ - }, { - key: "solve", value: function solve(nodesArray, edgesArray) { var ignoreClusters = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; @@ -41865,14 +41677,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "_getHighestEnergyNode", /** * get the node with the highest energy * @returns {*[]} * @private */ - }, { - key: "_getHighestEnergyNode", value: function _getHighestEnergyNode(ignoreClusters) { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; @@ -41904,6 +41716,8 @@ return /******/ (function(modules) { // webpackBootstrap return [maxEnergyNodeId, maxEnergy, dE_dx_max, dE_dy_max]; } + }, { + key: "_getEnergy", /** * calculate the energy of a single node @@ -41911,8 +41725,6 @@ return /******/ (function(modules) { // webpackBootstrap * @returns {*[]} * @private */ - }, { - key: "_getEnergy", value: function _getEnergy(m) { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; @@ -41935,6 +41747,8 @@ return /******/ (function(modules) { // webpackBootstrap var delta_m = Math.sqrt(Math.pow(dE_dx, 2) + Math.pow(dE_dy, 2)); return [delta_m, dE_dx, dE_dy]; } + }, { + key: "_moveNode", /** * move the node based on it's energy @@ -41944,8 +41758,6 @@ return /******/ (function(modules) { // webpackBootstrap * @param dE_dy * @private */ - }, { - key: "_moveNode", value: function _moveNode(m, dE_dx, dE_dy) { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; @@ -41981,14 +41793,14 @@ return /******/ (function(modules) { // webpackBootstrap nodes[m].x += dx; nodes[m].y += dy; } + }, { + key: "_createL_matrix", /** * Create the L matrix: edge length times shortest path * @param D_matrix * @private */ - }, { - key: "_createL_matrix", value: function _createL_matrix(D_matrix) { var nodesArray = this.body.nodeIndices; var edgeLength = this.springLength; @@ -42001,14 +41813,14 @@ return /******/ (function(modules) { // webpackBootstrap } } } + }, { + key: "_createK_matrix", /** * Create the K matrix: spring constants times shortest path * @param D_matrix * @private */ - }, { - key: "_createK_matrix", value: function _createK_matrix(D_matrix) { var nodesArray = this.body.nodeIndices; var edgeStrength = this.springConstant; @@ -42030,7 +41842,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 114 */ +/* 113 */ /***/ function(module, exports) { /** @@ -42101,7 +41913,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 115 */ +/* 114 */ /***/ function(module, exports) { /** @@ -42388,7 +42200,7 @@ return /******/ (function(modules) { // webpackBootstrap } /***/ }, -/* 116 */ +/* 115 */ /***/ function(module, exports) { /** @@ -42738,10 +42550,10 @@ return /******/ (function(modules) { // webpackBootstrap if (token === 'false') { token = false; // convert to boolean } else if (token === 'true') { - token = true; // convert to boolean - } else if (!isNaN(Number(token))) { - token = Number(token); // convert to number - } + token = true; // convert to boolean + } else if (!isNaN(Number(token))) { + token = Number(token); // convert to number + } tokenType = TOKENTYPE.IDENTIFIER; return; } @@ -42883,8 +42695,8 @@ return /******/ (function(modules) { // webpackBootstrap getToken(); // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " } else { - parseNodeStatement(graph, id); - } + parseNodeStatement(graph, id); + } } /** @@ -43286,7 +43098,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.DOTToGraph = DOTToGraph; /***/ }, -/* 117 */ +/* 116 */ /***/ function(module, exports) { 'use strict'; @@ -43364,7 +43176,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.parseGephi = parseGephi; /***/ }, -/* 118 */ +/* 117 */ /***/ function(module, exports) { /** @@ -43390,13 +43202,13 @@ return /******/ (function(modules) { // webpackBootstrap this.callback = callback; } - /** - * @param {string} url The Url to cache the image as - * @return {Image} imageToLoadBrokenUrlOn The image object - */ - _createClass(Images, [{ key: "_addImageToCache", + + /** + * @param {string} url The Url to cache the image as + * @return {Image} imageToLoadBrokenUrlOn The image object + */ value: function _addImageToCache(url, imageToCache) { // IE11 fix -- thanks dponch! if (imageToCache.width === 0) { @@ -43408,14 +43220,14 @@ return /******/ (function(modules) { // webpackBootstrap this.images[url] = imageToCache; } + }, { + key: "_tryloadBrokenUrl", /** * @param {string} url The original Url that failed to load, if the broken image is successfully loaded it will be added to the cache using this Url as the key so that subsequent requests for this Url will return the broken image * @param {string} brokenUrl Url the broken image to try and load * @return {Image} imageToLoadBrokenUrlOn The image object */ - }, { - key: "_tryloadBrokenUrl", value: function _tryloadBrokenUrl(url, brokenUrl, imageToLoadBrokenUrlOn) { var _this = this; @@ -43432,25 +43244,25 @@ return /******/ (function(modules) { // webpackBootstrap //Set the source of the image to the brokenUrl, this is actually what kicks off the loading of the broken image imageToLoadBrokenUrlOn.src = brokenUrl; } + }, { + key: "_redrawWithImage", /** * @return {Image} imageToRedrawWith The images that will be passed to the callback when it is invoked */ - }, { - key: "_redrawWithImage", value: function _redrawWithImage(imageToRedrawWith) { if (this.callback) { this.callback(imageToRedrawWith); } } + }, { + key: "load", /** * @param {string} url Url of the image * @param {string} brokenUrl Url of an image to use if the url image is not found * @return {Image} img The image object */ - }, { - key: "load", value: function load(url, brokenUrl, id) { var _this2 = this; @@ -43490,7 +43302,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 119 */ +/* 118 */ /***/ function(module, exports) { // English diff --git a/dist/vis.min.css b/dist/vis.min.css index 5779b369..554e3b1e 100644 --- a/dist/vis.min.css +++ b/dist/vis.min.css @@ -1 +1 @@ -.vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;height:13px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:transparent;width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{background:url(img/timeline/delete.png) center no-repeat;position:absolute;width:24px;height:24px;top:-4px;right:-24px;cursor:pointer}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;font-color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;margin-top:-140px;margin-left:30px;width:293px;height:425px;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow:after,div.vis-color-picker div.vis-arrow:before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} \ No newline at end of file +.vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:9pt}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:90pt;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:75pt}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;height:13px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:transparent;width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:1pc;width:1pc;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:1pc;width:1pc;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:9pt;margin-top:-9pt}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{background:url(img/timeline/delete.png) center no-repeat;position:absolute;width:24px;height:24px;top:-4px;right:-24px;cursor:pointer}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:9pt;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;font-color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;margin-top:-140px;margin-left:30px;width:293px;height:425px;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow:after,div.vis-color-picker div.vis-arrow:before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} \ No newline at end of file diff --git a/dist/vis.physics.worker.js b/dist/vis.physics.worker.js deleted file mode 100644 index 3f31de26..00000000 --- a/dist/vis.physics.worker.js +++ /dev/null @@ -1,1484 +0,0 @@ -/** - * vis.js - * https://github.com/almende/vis - * - * A dynamic, browser-based visualization library. - * - * @version 4.9.1-SNAPSHOT - * @date 2015-10-05 - * - * @license - * Copyright (C) 2011-2015 Almende B.V, http://almende.com - * - * Vis.js is dual licensed under both - * - * * The Apache 2.0 License - * http://www.apache.org/licenses/LICENSE-2.0 - * - * and - * - * * The MIT License - * http://opensource.org/licenses/MIT - * - * Vis.js may be distributed under either license. - */ - -"use strict"; - -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _PhysicsWorkerJs = __webpack_require__(1); - - var _PhysicsWorkerJs2 = _interopRequireDefault(_PhysicsWorkerJs); - - var physicsWorker = new _PhysicsWorkerJs2['default'](function (data) { - return postMessage(data); - }); - self.addEventListener('message', function (event) { - return physicsWorker.handleMessage(event); - }, false); - -/***/ }, -/* 1 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - Object.defineProperty(exports, '__esModule', { - value: true - }); - - 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 _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - - var _componentsPhysicsBarnesHutSolver = __webpack_require__(2); - - var _componentsPhysicsBarnesHutSolver2 = _interopRequireDefault(_componentsPhysicsBarnesHutSolver); - - var _componentsPhysicsRepulsionSolver = __webpack_require__(3); - - var _componentsPhysicsRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsRepulsionSolver); - - var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(4); - - var _componentsPhysicsHierarchicalRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalRepulsionSolver); - - var _componentsPhysicsSpringSolver = __webpack_require__(5); - - var _componentsPhysicsSpringSolver2 = _interopRequireDefault(_componentsPhysicsSpringSolver); - - var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(6); - - var _componentsPhysicsHierarchicalSpringSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalSpringSolver); - - var _componentsPhysicsCentralGravitySolver = __webpack_require__(7); - - var _componentsPhysicsCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsCentralGravitySolver); - - var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(8); - - var _componentsPhysicsFA2BasedRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedRepulsionSolver); - - var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(9); - - var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver); - - var PhysicsWorker = (function () { - function PhysicsWorker(postMessage) { - _classCallCheck(this, PhysicsWorker); - - this.body = {}; - this.physicsBody = { physicsNodeIndices: [], physicsEdgeIndices: [], forces: {}, velocities: {} }; - this.postMessage = postMessage; - this.options = {}; - this.stabilized = false; - this.previousStates = {}; - this.positions = {}; - this.timestep = 0.5; - } - - _createClass(PhysicsWorker, [{ - key: 'handleMessage', - value: function handleMessage(event) { - var msg = event.data; - switch (msg.type) { - case 'calculateForces': - this.calculateForces(); - this.moveNodes(); - this.postMessage({ - type: 'positions', - data: { - positions: this.positions, - stabilized: this.stabilized - } - }); - break; - case 'update': - var node = this.body.nodes[msg.data.id]; - node.x = msg.data.x; - node.y = msg.data.y; - break; - case 'options': - this.options = msg.data; - this.timestep = this.options.timestep; - this.init(); - break; - case 'physicsObjects': - this.body.nodes = msg.data.nodes; - this.body.edges = msg.data.edges; - this.updatePhysicsData(); - break; - default: - console.warn('unknown message from PhysicsEngine', msg); - } - } - - /** - * configure the engine. - */ - }, { - key: 'init', - value: function init() { - var options; - if (this.options.solver === 'forceAtlas2Based') { - options = this.options.forceAtlas2Based; - this.nodesSolver = new _componentsPhysicsFA2BasedRepulsionSolver2['default'](this.body, this.physicsBody, options); - this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options); - this.gravitySolver = new _componentsPhysicsFA2BasedCentralGravitySolver2['default'](this.body, this.physicsBody, options); - } else if (this.options.solver === 'repulsion') { - options = this.options.repulsion; - this.nodesSolver = new _componentsPhysicsRepulsionSolver2['default'](this.body, this.physicsBody, options); - this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options); - this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options); - } else if (this.options.solver === 'hierarchicalRepulsion') { - options = this.options.hierarchicalRepulsion; - this.nodesSolver = new _componentsPhysicsHierarchicalRepulsionSolver2['default'](this.body, this.physicsBody, options); - this.edgesSolver = new _componentsPhysicsHierarchicalSpringSolver2['default'](this.body, this.physicsBody, options); - this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options); - } else { - // barnesHut - options = this.options.barnesHut; - this.nodesSolver = new _componentsPhysicsBarnesHutSolver2['default'](this.body, this.physicsBody, options); - this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options); - this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options); - } - - this.modelOptions = options; - } - - /** - * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. - * - * @private - */ - }, { - key: 'updatePhysicsData', - value: function updatePhysicsData() { - this.physicsBody.forces = {}; - this.physicsBody.physicsNodeIndices = []; - this.physicsBody.physicsEdgeIndices = []; - var nodes = this.body.nodes; - var edges = this.body.edges; - - // get node indices for physics - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - this.physicsBody.physicsNodeIndices.push(nodeId); - this.positions[nodeId] = { - x: nodes[nodeId].x, - y: nodes[nodeId].y - }; - } - } - - // get edge indices for physics - for (var edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - this.physicsBody.physicsEdgeIndices.push(edgeId); - } - } - - // get the velocity and the forces vector - for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { - var nodeId = this.physicsBody.physicsNodeIndices[i]; - this.physicsBody.forces[nodeId] = { x: 0, y: 0 }; - - // forces can be reset because they are recalculated. Velocities have to persist. - if (this.physicsBody.velocities[nodeId] === undefined) { - this.physicsBody.velocities[nodeId] = { x: 0, y: 0 }; - } - } - - // clean deleted nodes from the velocity vector - for (var nodeId in this.physicsBody.velocities) { - if (nodes[nodeId] === undefined) { - delete this.physicsBody.velocities[nodeId]; - } - } - // console.log(this.physicsBody); - } - - /** - * move the nodes one timestap and check if they are stabilized - * @returns {boolean} - */ - }, { - key: 'moveNodes', - value: function moveNodes() { - var nodeIndices = this.physicsBody.physicsNodeIndices; - var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; - var maxNodeVelocity = 0; - - for (var i = 0; i < nodeIndices.length; i++) { - var nodeId = nodeIndices[i]; - var nodeVelocity = this._performStep(nodeId, maxVelocity); - // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized - maxNodeVelocity = Math.max(maxNodeVelocity, nodeVelocity); - } - - // evaluating the stabilized and adaptiveTimestepEnabled conditions - this.stabilized = maxNodeVelocity < this.options.minVelocity; - } - - /** - * Perform the actual step - * - * @param nodeId - * @param maxVelocity - * @returns {number} - * @private - */ - }, { - key: '_performStep', - value: function _performStep(nodeId, maxVelocity) { - var node = this.body.nodes[nodeId]; - var timestep = this.timestep; - var forces = this.physicsBody.forces; - var velocities = this.physicsBody.velocities; - - // store the state so we can revert - this.previousStates[nodeId] = { x: node.x, y: node.y, vx: velocities[nodeId].x, vy: velocities[nodeId].y }; - - if (node.options.fixed.x === false) { - var dx = this.modelOptions.damping * velocities[nodeId].x; // damping force - var ax = (forces[nodeId].x - dx) / node.options.mass; // acceleration - velocities[nodeId].x += ax * timestep; // velocity - velocities[nodeId].x = Math.abs(velocities[nodeId].x) > maxVelocity ? velocities[nodeId].x > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].x; - node.x += velocities[nodeId].x * timestep; // position - this.positions[nodeId].x = node.x; - } else { - forces[nodeId].x = 0; - velocities[nodeId].x = 0; - } - - if (node.options.fixed.y === false) { - var dy = this.modelOptions.damping * velocities[nodeId].y; // damping force - var ay = (forces[nodeId].y - dy) / node.options.mass; // acceleration - velocities[nodeId].y += ay * timestep; // velocity - velocities[nodeId].y = Math.abs(velocities[nodeId].y) > maxVelocity ? velocities[nodeId].y > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].y; - node.y += velocities[nodeId].y * timestep; // position - this.positions[nodeId].y = node.y; - } else { - forces[nodeId].y = 0; - velocities[nodeId].y = 0; - } - - var totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x, 2) + Math.pow(velocities[nodeId].y, 2)); - return totalVelocity; - } - - /** - * calculate the forces for one physics iteration. - */ - }, { - key: 'calculateForces', - value: function calculateForces() { - this.gravitySolver.solve(); - this.nodesSolver.solve(); - this.edgesSolver.solve(); - } - }]); - - return PhysicsWorker; - })(); - - exports['default'] = PhysicsWorker; - module.exports = exports['default']; - -/***/ }, -/* 2 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - 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 BarnesHutSolver = (function () { - function BarnesHutSolver(body, physicsBody, options) { - _classCallCheck(this, BarnesHutSolver); - - this.body = body; - this.physicsBody = physicsBody; - this.barnesHutTree; - this.setOptions(options); - this.randomSeed = 5; - } - - _createClass(BarnesHutSolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; - this.thetaInversed = 1 / this.options.theta; - this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius - } - }, { - key: "seededRandom", - value: function seededRandom() { - var x = Math.sin(this.randomSeed++) * 10000; - return x - Math.floor(x); - } - - /** - * This function calculates the forces the nodes apply on eachother based on a gravitational model. - * The Barnes Hut method is used to speed up this N-body simulation. - * - * @private - */ - }, { - key: "solve", - value: function solve() { - if (this.options.gravitationalConstant !== 0 && this.physicsBody.physicsNodeIndices.length > 0) { - var node = undefined; - var nodes = this.body.nodes; - var nodeIndices = this.physicsBody.physicsNodeIndices; - var nodeCount = nodeIndices.length; - - // create the tree - var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices); - - // for debugging - this.barnesHutTree = barnesHutTree; - - // place the nodes one by one recursively - for (var i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - if (node.options.mass > 0) { - // starting with root is irrelevant, it never passes the BarnesHutSolver condition - this._getForceContribution(barnesHutTree.root.children.NW, node); - this._getForceContribution(barnesHutTree.root.children.NE, node); - this._getForceContribution(barnesHutTree.root.children.SW, node); - this._getForceContribution(barnesHutTree.root.children.SE, node); - } - } - } - } - - /** - * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. - * If a region contains a single node, we check if it is not itself, then we apply the force. - * - * @param parentBranch - * @param node - * @private - */ - }, { - key: "_getForceContribution", - value: function _getForceContribution(parentBranch, node) { - // we get no force contribution from an empty region - if (parentBranch.childrenCount > 0) { - var dx = undefined, - dy = undefined, - distance = undefined; - - // get the distance from the center of mass to the node. - dx = parentBranch.centerOfMass.x - node.x; - dy = parentBranch.centerOfMass.y - node.y; - distance = Math.sqrt(dx * dx + dy * dy); - - // BarnesHutSolver condition - // original condition : s/d < theta = passed === d/s > 1/theta = passed - // calcSize = 1/s --> d * 1/s > 1/theta = passed - if (distance * parentBranch.calcSize > this.thetaInversed) { - this._calculateForces(distance, dx, dy, node, parentBranch); - } else { - // Did not pass the condition, go into children if available - if (parentBranch.childrenCount === 4) { - this._getForceContribution(parentBranch.children.NW, node); - this._getForceContribution(parentBranch.children.NE, node); - this._getForceContribution(parentBranch.children.SW, node); - this._getForceContribution(parentBranch.children.SE, node); - } else { - // parentBranch must have only one node, if it was empty we wouldnt be here - if (parentBranch.children.data.id != node.id) { - // if it is not self - this._calculateForces(distance, dx, dy, node, parentBranch); - } - } - } - } - } - - /** - * Calculate the forces based on the distance. - * - * @param distance - * @param dx - * @param dy - * @param node - * @param parentBranch - * @private - */ - }, { - key: "_calculateForces", - value: function _calculateForces(distance, dx, dy, node, parentBranch) { - if (distance === 0) { - distance = 0.1; - dx = distance; - } - - if (this.overlapAvoidanceFactor < 1) { - distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); - } - - // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines - // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce - var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance, 3); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - - this.physicsBody.forces[node.id].x += fx; - this.physicsBody.forces[node.id].y += fy; - } - - /** - * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. - * - * @param nodes - * @param nodeIndices - * @private - */ - }, { - key: "_formBarnesHutTree", - value: function _formBarnesHutTree(nodes, nodeIndices) { - var node = undefined; - var nodeCount = nodeIndices.length; - - var minX = nodes[nodeIndices[0]].x; - var minY = nodes[nodeIndices[0]].y; - var maxX = nodes[nodeIndices[0]].x; - var maxY = nodes[nodeIndices[0]].y; - - // get the range of the nodes - for (var i = 1; i < nodeCount; i++) { - var x = nodes[nodeIndices[i]].x; - var y = nodes[nodeIndices[i]].y; - if (nodes[nodeIndices[i]].options.mass > 0) { - if (x < minX) { - minX = x; - } - if (x > maxX) { - maxX = x; - } - if (y < minY) { - minY = y; - } - if (y > maxY) { - maxY = y; - } - } - } - // make the range a square - var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y - if (sizeDiff > 0) { - minY -= 0.5 * sizeDiff; - maxY += 0.5 * sizeDiff; - } // xSize > ySize - else { - minX += 0.5 * sizeDiff; - maxX -= 0.5 * sizeDiff; - } // xSize < ySize - - var minimumTreeSize = 1e-5; - var rootSize = Math.max(minimumTreeSize, Math.abs(maxX - minX)); - var halfRootSize = 0.5 * rootSize; - var centerX = 0.5 * (minX + maxX), - centerY = 0.5 * (minY + maxY); - - // construct the barnesHutTree - var barnesHutTree = { - root: { - centerOfMass: { x: 0, y: 0 }, - mass: 0, - range: { - minX: centerX - halfRootSize, maxX: centerX + halfRootSize, - minY: centerY - halfRootSize, maxY: centerY + halfRootSize - }, - size: rootSize, - calcSize: 1 / rootSize, - children: { data: null }, - maxWidth: 0, - level: 0, - childrenCount: 4 - } - }; - this._splitBranch(barnesHutTree.root); - - // place the nodes one by one recursively - for (var i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - if (node.options.mass > 0) { - this._placeInTree(barnesHutTree.root, node); - } - } - - // make global - return barnesHutTree; - } - - /** - * this updates the mass of a branch. this is increased by adding a node. - * - * @param parentBranch - * @param node - * @private - */ - }, { - key: "_updateBranchMass", - value: function _updateBranchMass(parentBranch, node) { - var totalMass = parentBranch.mass + node.options.mass; - var totalMassInv = 1 / totalMass; - - parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.options.mass; - parentBranch.centerOfMass.x *= totalMassInv; - - parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.options.mass; - parentBranch.centerOfMass.y *= totalMassInv; - - parentBranch.mass = totalMass; - var biggestSize = Math.max(Math.max(node.height, node.radius), node.width); - parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth; - } - - /** - * determine in which branch the node will be placed. - * - * @param parentBranch - * @param node - * @param skipMassUpdate - * @private - */ - }, { - key: "_placeInTree", - value: function _placeInTree(parentBranch, node, skipMassUpdate) { - if (skipMassUpdate != true || skipMassUpdate === undefined) { - // update the mass of the branch. - this._updateBranchMass(parentBranch, node); - } - - if (parentBranch.children.NW.range.maxX > node.x) { - // in NW or SW - if (parentBranch.children.NW.range.maxY > node.y) { - // in NW - this._placeInRegion(parentBranch, node, "NW"); - } else { - // in SW - this._placeInRegion(parentBranch, node, "SW"); - } - } else { - // in NE or SE - if (parentBranch.children.NW.range.maxY > node.y) { - // in NE - this._placeInRegion(parentBranch, node, "NE"); - } else { - // in SE - this._placeInRegion(parentBranch, node, "SE"); - } - } - } - - /** - * actually place the node in a region (or branch) - * - * @param parentBranch - * @param node - * @param region - * @private - */ - }, { - key: "_placeInRegion", - value: function _placeInRegion(parentBranch, node, region) { - switch (parentBranch.children[region].childrenCount) { - case 0: - // place node here - parentBranch.children[region].children.data = node; - parentBranch.children[region].childrenCount = 1; - this._updateBranchMass(parentBranch.children[region], node); - break; - case 1: - // convert into children - // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) - // we move one node a pixel and we do not put it in the tree. - if (parentBranch.children[region].children.data.x === node.x && parentBranch.children[region].children.data.y === node.y) { - node.x += this.seededRandom(); - node.y += this.seededRandom(); - } else { - this._splitBranch(parentBranch.children[region]); - this._placeInTree(parentBranch.children[region], node); - } - break; - case 4: - // place in branch - this._placeInTree(parentBranch.children[region], node); - break; - } - } - - /** - * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch - * after the split is complete. - * - * @param parentBranch - * @private - */ - }, { - key: "_splitBranch", - value: function _splitBranch(parentBranch) { - // if the branch is shaded with a node, replace the node in the new subset. - var containedNode = null; - if (parentBranch.childrenCount === 1) { - containedNode = parentBranch.children.data; - parentBranch.mass = 0; - parentBranch.centerOfMass.x = 0; - parentBranch.centerOfMass.y = 0; - } - parentBranch.childrenCount = 4; - parentBranch.children.data = null; - this._insertRegion(parentBranch, "NW"); - this._insertRegion(parentBranch, "NE"); - this._insertRegion(parentBranch, "SW"); - this._insertRegion(parentBranch, "SE"); - - if (containedNode != null) { - this._placeInTree(parentBranch, containedNode); - } - } - - /** - * This function subdivides the region into four new segments. - * Specifically, this inserts a single new segment. - * It fills the children section of the parentBranch - * - * @param parentBranch - * @param region - * @param parentRange - * @private - */ - }, { - key: "_insertRegion", - value: function _insertRegion(parentBranch, region) { - var minX = undefined, - maxX = undefined, - minY = undefined, - maxY = undefined; - var childSize = 0.5 * parentBranch.size; - switch (region) { - case "NW": - minX = parentBranch.range.minX; - maxX = parentBranch.range.minX + childSize; - minY = parentBranch.range.minY; - maxY = parentBranch.range.minY + childSize; - break; - case "NE": - minX = parentBranch.range.minX + childSize; - maxX = parentBranch.range.maxX; - minY = parentBranch.range.minY; - maxY = parentBranch.range.minY + childSize; - break; - case "SW": - minX = parentBranch.range.minX; - maxX = parentBranch.range.minX + childSize; - minY = parentBranch.range.minY + childSize; - maxY = parentBranch.range.maxY; - break; - case "SE": - minX = parentBranch.range.minX + childSize; - maxX = parentBranch.range.maxX; - minY = parentBranch.range.minY + childSize; - maxY = parentBranch.range.maxY; - break; - } - - parentBranch.children[region] = { - centerOfMass: { x: 0, y: 0 }, - mass: 0, - range: { minX: minX, maxX: maxX, minY: minY, maxY: maxY }, - size: 0.5 * parentBranch.size, - calcSize: 2 * parentBranch.calcSize, - children: { data: null }, - maxWidth: 0, - level: parentBranch.level + 1, - childrenCount: 0 - }; - } - - //--------------------------- DEBUGGING BELOW ---------------------------// - - /** - * This function is for debugging purposed, it draws the tree. - * - * @param ctx - * @param color - * @private - */ - }, { - key: "_debug", - value: function _debug(ctx, color) { - if (this.barnesHutTree !== undefined) { - - ctx.lineWidth = 1; - - this._drawBranch(this.barnesHutTree.root, ctx, color); - } - } - - /** - * This function is for debugging purposes. It draws the branches recursively. - * - * @param branch - * @param ctx - * @param color - * @private - */ - }, { - key: "_drawBranch", - value: function _drawBranch(branch, ctx, color) { - if (color === undefined) { - color = "#FF0000"; - } - - if (branch.childrenCount === 4) { - this._drawBranch(branch.children.NW, ctx); - this._drawBranch(branch.children.NE, ctx); - this._drawBranch(branch.children.SE, ctx); - this._drawBranch(branch.children.SW, ctx); - } - ctx.strokeStyle = color; - ctx.beginPath(); - ctx.moveTo(branch.range.minX, branch.range.minY); - ctx.lineTo(branch.range.maxX, branch.range.minY); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(branch.range.maxX, branch.range.minY); - ctx.lineTo(branch.range.maxX, branch.range.maxY); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(branch.range.maxX, branch.range.maxY); - ctx.lineTo(branch.range.minX, branch.range.maxY); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(branch.range.minX, branch.range.maxY); - ctx.lineTo(branch.range.minX, branch.range.minY); - ctx.stroke(); - - /* - if (branch.mass > 0) { - ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); - ctx.stroke(); - } - */ - } - }]); - - return BarnesHutSolver; - })(); - - exports["default"] = BarnesHutSolver; - module.exports = exports["default"]; - -/***/ }, -/* 3 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - 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 RepulsionSolver = (function () { - function RepulsionSolver(body, physicsBody, options) { - _classCallCheck(this, RepulsionSolver); - - this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); - } - - _createClass(RepulsionSolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; - } - - /** - * Calculate the forces the nodes apply on each other based on a repulsion field. - * This field is linearly approximated. - * - * @private - */ - }, { - key: "solve", - value: function solve() { - var dx, dy, distance, fx, fy, repulsingForce, node1, node2; - - var nodes = this.body.nodes; - var nodeIndices = this.physicsBody.physicsNodeIndices; - var forces = this.physicsBody.forces; - - // repulsing forces between nodes - var nodeDistance = this.options.nodeDistance; - - // approximation constants - var a = -2 / 3 / nodeDistance; - var b = 4 / 3; - - // we loop from i over all but the last entree in the array - // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j - for (var i = 0; i < nodeIndices.length - 1; i++) { - node1 = nodes[nodeIndices[i]]; - for (var j = i + 1; j < nodeIndices.length; j++) { - node2 = nodes[nodeIndices[j]]; - - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); - - // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping. - if (distance === 0) { - distance = 0.1 * Math.random(); - dx = distance; - } - - if (distance < 2 * nodeDistance) { - if (distance < 0.5 * nodeDistance) { - repulsingForce = 1.0; - } else { - repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / nodeDistance - 1) * steepness)) - } - repulsingForce = repulsingForce / distance; - - fx = dx * repulsingForce; - fy = dy * repulsingForce; - - forces[node1.id].x -= fx; - forces[node1.id].y -= fy; - forces[node2.id].x += fx; - forces[node2.id].y += fy; - } - } - } - } - }]); - - return RepulsionSolver; - })(); - - exports["default"] = RepulsionSolver; - module.exports = exports["default"]; - -/***/ }, -/* 4 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - 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 HierarchicalRepulsionSolver = (function () { - function HierarchicalRepulsionSolver(body, physicsBody, options) { - _classCallCheck(this, HierarchicalRepulsionSolver); - - this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); - } - - _createClass(HierarchicalRepulsionSolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; - } - - /** - * Calculate the forces the nodes apply on each other based on a repulsion field. - * This field is linearly approximated. - * - * @private - */ - }, { - key: "solve", - value: function solve() { - var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j; - - var nodes = this.body.nodes; - var nodeIndices = this.physicsBody.physicsNodeIndices; - var forces = this.physicsBody.forces; - - // repulsing forces between nodes - var nodeDistance = this.options.nodeDistance; - - // we loop from i over all but the last entree in the array - // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j - for (i = 0; i < nodeIndices.length - 1; i++) { - node1 = nodes[nodeIndices[i]]; - for (j = i + 1; j < nodeIndices.length; j++) { - node2 = nodes[nodeIndices[j]]; - - // nodes only affect nodes on their level - if (node1.level === node2.level) { - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); - - var steepness = 0.05; - if (distance < nodeDistance) { - repulsingForce = -Math.pow(steepness * distance, 2) + Math.pow(steepness * nodeDistance, 2); - } else { - repulsingForce = 0; - } - // normalize force with - if (distance === 0) { - distance = 0.01; - } else { - repulsingForce = repulsingForce / distance; - } - fx = dx * repulsingForce; - fy = dy * repulsingForce; - - forces[node1.id].x -= fx; - forces[node1.id].y -= fy; - forces[node2.id].x += fx; - forces[node2.id].y += fy; - } - } - } - } - }]); - - return HierarchicalRepulsionSolver; - })(); - - exports["default"] = HierarchicalRepulsionSolver; - module.exports = exports["default"]; - -/***/ }, -/* 5 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - 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 SpringSolver = (function () { - function SpringSolver(body, physicsBody, options) { - _classCallCheck(this, SpringSolver); - - this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); - } - - _createClass(SpringSolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; - } - - /** - * This function calculates the springforces on the nodes, accounting for the support nodes. - * - * @private - */ - }, { - key: "solve", - value: function solve() { - var edgeLength = undefined, - edge = undefined; - var edgeIndices = this.physicsBody.physicsEdgeIndices; - var edges = this.body.edges; - var nodes = this.body.nodes; - var node1 = undefined, - node2 = undefined, - node3 = undefined; - - // forces caused by the edges, modelled as springs - for (var i = 0; i < edgeIndices.length; i++) { - edge = edges[edgeIndices[i]]; - if (edge.connected === true && edge.toId !== edge.fromId) { - // only calculate forces if nodes are in the same sector - if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) { - if (edge.edgeType.via !== undefined) { - edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; - node1 = nodes[edge.to.id]; - node2 = nodes[edge.edgeType.via.id]; - node3 = nodes[edge.from.id]; - - this._calculateSpringForce(node1, node2, 0.5 * edgeLength); - this._calculateSpringForce(node2, node3, 0.5 * edgeLength); - } else { - // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use - // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger. - edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5 : edge.options.length; - this._calculateSpringForce(nodes[edge.from.id], nodes[edge.to.id], edgeLength); - } - } - } - } - } - - /** - * This is the code actually performing the calculation for the function above. - * - * @param node1 - * @param node2 - * @param edgeLength - * @private - */ - }, { - key: "_calculateSpringForce", - value: function _calculateSpringForce(node1, node2, edgeLength) { - var dx = node1.x - node2.x; - var dy = node1.y - node2.y; - var distance = Math.max(Math.sqrt(dx * dx + dy * dy), 0.01); - - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - var springForce = this.options.springConstant * (edgeLength - distance) / distance; - - var fx = dx * springForce; - var fy = dy * springForce; - - // handle the case where one node is not part of the physcis - if (this.physicsBody.forces[node1.id] !== undefined) { - this.physicsBody.forces[node1.id].x += fx; - this.physicsBody.forces[node1.id].y += fy; - } - - if (this.physicsBody.forces[node2.id] !== undefined) { - this.physicsBody.forces[node2.id].x -= fx; - this.physicsBody.forces[node2.id].y -= fy; - } - } - }]); - - return SpringSolver; - })(); - - exports["default"] = SpringSolver; - module.exports = exports["default"]; - -/***/ }, -/* 6 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - 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 HierarchicalSpringSolver = (function () { - function HierarchicalSpringSolver(body, physicsBody, options) { - _classCallCheck(this, HierarchicalSpringSolver); - - this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); - } - - _createClass(HierarchicalSpringSolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; - } - - /** - * This function calculates the springforces on the nodes, accounting for the support nodes. - * - * @private - */ - }, { - key: "solve", - value: function solve() { - var edgeLength, edge; - var dx, dy, fx, fy, springForce, distance; - var edges = this.body.edges; - var factor = 0.5; - - var edgeIndices = this.physicsBody.physicsEdgeIndices; - var nodeIndices = this.physicsBody.physicsNodeIndices; - var forces = this.physicsBody.forces; - - // initialize the spring force counters - for (var i = 0; i < nodeIndices.length; i++) { - var nodeId = nodeIndices[i]; - forces[nodeId].springFx = 0; - forces[nodeId].springFy = 0; - } - - // forces caused by the edges, modelled as springs - for (var i = 0; i < edgeIndices.length; i++) { - edge = edges[edgeIndices[i]]; - if (edge.connected === true) { - edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; - - dx = edge.from.x - edge.to.x; - dy = edge.from.y - edge.to.y; - distance = Math.sqrt(dx * dx + dy * dy); - distance = distance === 0 ? 0.01 : distance; - - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.options.springConstant * (edgeLength - distance) / distance; - - fx = dx * springForce; - fy = dy * springForce; - - if (edge.to.level != edge.from.level) { - if (forces[edge.toId] !== undefined) { - forces[edge.toId].springFx -= fx; - forces[edge.toId].springFy -= fy; - } - if (forces[edge.fromId] !== undefined) { - forces[edge.fromId].springFx += fx; - forces[edge.fromId].springFy += fy; - } - } else { - if (forces[edge.toId] !== undefined) { - forces[edge.toId].x -= factor * fx; - forces[edge.toId].y -= factor * fy; - } - if (forces[edge.fromId] !== undefined) { - forces[edge.fromId].x += factor * fx; - forces[edge.fromId].y += factor * fy; - } - } - } - } - - // normalize spring forces - var springForce = 1; - var springFx, springFy; - for (var i = 0; i < nodeIndices.length; i++) { - var nodeId = nodeIndices[i]; - springFx = Math.min(springForce, Math.max(-springForce, forces[nodeId].springFx)); - springFy = Math.min(springForce, Math.max(-springForce, forces[nodeId].springFy)); - - forces[nodeId].x += springFx; - forces[nodeId].y += springFy; - } - - // retain energy balance - var totalFx = 0; - var totalFy = 0; - for (var i = 0; i < nodeIndices.length; i++) { - var nodeId = nodeIndices[i]; - totalFx += forces[nodeId].x; - totalFy += forces[nodeId].y; - } - var correctionFx = totalFx / nodeIndices.length; - var correctionFy = totalFy / nodeIndices.length; - - for (var i = 0; i < nodeIndices.length; i++) { - var nodeId = nodeIndices[i]; - forces[nodeId].x -= correctionFx; - forces[nodeId].y -= correctionFy; - } - } - }]); - - return HierarchicalSpringSolver; - })(); - - exports["default"] = HierarchicalSpringSolver; - module.exports = exports["default"]; - -/***/ }, -/* 7 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - 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 CentralGravitySolver = (function () { - function CentralGravitySolver(body, physicsBody, options) { - _classCallCheck(this, CentralGravitySolver); - - this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); - } - - _createClass(CentralGravitySolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; - } - }, { - key: "solve", - value: function solve() { - var dx = undefined, - dy = undefined, - distance = undefined, - node = undefined; - var nodes = this.body.nodes; - var nodeIndices = this.physicsBody.physicsNodeIndices; - var forces = this.physicsBody.forces; - - for (var i = 0; i < nodeIndices.length; i++) { - var nodeId = nodeIndices[i]; - node = nodes[nodeId]; - dx = -node.x; - dy = -node.y; - distance = Math.sqrt(dx * dx + dy * dy); - - this._calculateForces(distance, dx, dy, forces, node); - } - } - - /** - * Calculate the forces based on the distance. - * @private - */ - }, { - key: "_calculateForces", - value: function _calculateForces(distance, dx, dy, forces, node) { - var gravityForce = distance === 0 ? 0 : this.options.centralGravity / distance; - forces[node.id].x = dx * gravityForce; - forces[node.id].y = dy * gravityForce; - } - }]); - - return CentralGravitySolver; - })(); - - exports["default"] = CentralGravitySolver; - module.exports = exports["default"]; - -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - 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; }; })(); - - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - - var _BarnesHutSolver2 = __webpack_require__(2); - - var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); - - var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) { - _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); - - function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { - _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); - - _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); - } - - /** - * Calculate the forces based on the distance. - * - * @param distance - * @param dx - * @param dy - * @param node - * @param parentBranch - * @private - */ - - _createClass(ForceAtlas2BasedRepulsionSolver, [{ - key: "_calculateForces", - value: function _calculateForces(distance, dx, dy, node, parentBranch) { - if (distance === 0) { - distance = 0.1 * Math.random(); - dx = distance; - } - - if (this.overlapAvoidanceFactor < 1) { - distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); - } - - var degree = node.edges.length + 1; - // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines - // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce - var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass * degree / Math.pow(distance, 2); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - - this.physicsBody.forces[node.id].x += fx; - this.physicsBody.forces[node.id].y += fy; - } - }]); - - return ForceAtlas2BasedRepulsionSolver; - })(_BarnesHutSolver3["default"]); - - exports["default"] = ForceAtlas2BasedRepulsionSolver; - module.exports = exports["default"]; - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - 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; }; })(); - - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - - var _CentralGravitySolver2 = __webpack_require__(7); - - var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2); - - var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) { - _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); - - function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { - _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver); - - _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); - } - - /** - * Calculate the forces based on the distance. - * @private - */ - - _createClass(ForceAtlas2BasedCentralGravitySolver, [{ - key: "_calculateForces", - value: function _calculateForces(distance, dx, dy, forces, node) { - if (distance > 0) { - var degree = node.edges.length + 1; - var gravityForce = this.options.centralGravity * degree * node.options.mass; - forces[node.id].x = dx * gravityForce; - forces[node.id].y = dy * gravityForce; - } - } - }]); - - return ForceAtlas2BasedCentralGravitySolver; - })(_CentralGravitySolver3["default"]); - - exports["default"] = ForceAtlas2BasedCentralGravitySolver; - module.exports = exports["default"]; - -/***/ } -/******/ ]); \ No newline at end of file From 04866eb02a14db392d2efb2ed4e133ff420e609a Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Tue, 6 Oct 2015 10:11:17 -0400 Subject: [PATCH 04/17] another warning in documentation --- docs/network/physics.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/network/physics.html b/docs/network/physics.html index f38fc3e9..0e609ca3 100644 --- a/docs/network/physics.html +++ b/docs/network/physics.html @@ -210,7 +210,8 @@ network.setOptions(options); path resolution. If the worker fails for any reason, the system will fall back to standard physics calculations. WorkInProgress: This has only been tested with default physics selections and does not attempt to optimize - stabilization. + stabilization. Has not been optimized for small changes to underlying dataset, so each change will + cause all data to be recopied to the worker thread. From eb181ba21eafe783f6738f2ff4fa23f1900bd4d3 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Tue, 6 Oct 2015 14:34:37 -0400 Subject: [PATCH 05/17] bug fix for interaction, was missing the length property on draggingNodes clearing forces/velocities on manually changed nodes to smooth out physics response. --- lib/network/modules/PhysicsEngine.js | 42 +++++++++++++++++----------- lib/network/modules/PhysicsWorker.js | 2 ++ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index c1ea64fe..4be2bc0a 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -117,7 +117,13 @@ class PhysicsEngine { }); // For identifying which nodes to send to worker thread this.body.emitter.on('dragStart', (properties) => {this.draggingNodes = properties.nodes;}); - this.body.emitter.on('dragEnd', () => {this.draggingNodes = [];}); + this.body.emitter.on('dragEnd', () => { + // need one last update to handle the case where a drag happens + // and the user holds the node clicked at the final position + // for a time prior to releasing + this.updateWorkerPositions(); + this.draggingNodes = []; + }); this.body.emitter.on('destroy', () => { if (this.physicsWorker) { this.physicsWorker.terminate(); @@ -244,8 +250,7 @@ class PhysicsEngine { case 'positions': this.stabilized = msg.data.stabilized; var positions = msg.data.positions; - // console.log('received positions', positions); - for (let i = 0; i < this.draggingNodes; i++) { + for (let i = 0; i < this.draggingNodes.length; i++) { delete positions[this.draggingNodes[i]]; } let nodeIds = Object.keys(positions); @@ -290,20 +295,7 @@ class PhysicsEngine { */ startSimulation() { if (this.physicsEnabled === true && this.options.enabled === true) { - if (this.physicsWorker) { - for(let i = 0; i < this.draggingNodes.length; i++) { - let nodeId = this.draggingNodes[i]; - let node = this.body.nodes[nodeId]; - this.physicsWorker.postMessage({ - type: 'update', - data: { - id: nodeId, - x: node.x, - y: node.y - } - }); - } - } + this.updateWorkerPositions(); this.stabilized = false; // when visible, adaptivity is disabled. @@ -577,6 +569,22 @@ class PhysicsEngine { } } + updateWorkerPositions() { + if (this.physicsWorker) { + for(let i = 0; i < this.draggingNodes.length; i++) { + let nodeId = this.draggingNodes[i]; + let node = this.body.nodes[nodeId]; + this.physicsWorker.postMessage({ + type: 'update', + data: { + id: nodeId, + x: node.x, + y: node.y + } + }); + } + } + } /** * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index f176a11c..80ee0877 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -37,6 +37,8 @@ class PhysicsWorker { let node = this.body.nodes[msg.data.id]; node.x = msg.data.x; node.y = msg.data.y; + this.physicsBody.forces[node.id] = {x: 0, y: 0}; + this.physicsBody.velocities[node.id] = {x: 0, y: 0}; break; case 'options': this.options = msg.data; From e1d6e057476e5061faa7126738d007223f6a88ef Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Tue, 6 Oct 2015 16:29:52 -0400 Subject: [PATCH 06/17] additional logic to handle changing fixed nodes --- lib/network/modules/PhysicsEngine.js | 50 +++++++++++++++++++++++----- lib/network/modules/PhysicsWorker.js | 23 ++++++++----- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index 4be2bc0a..1ad93783 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -384,6 +384,7 @@ class PhysicsEngine { } if (this.stabilized === false) { + this.updateWorkerFixed(); // adaptivity means the timestep adapts to the situation, only applicable for stabilization if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) { // this is the factor for increasing the timestep on success. @@ -464,15 +465,21 @@ class PhysicsEngine { let nodes = this.body.nodes; let edges = this.body.edges; + this.physicsBody.forces = {}; + this.physicsBody.physicsNodeIndices = []; + this.physicsBody.physicsEdgeIndices = []; + if (this.physicsWorker) { - var physicsWorkerNodes = {}; + this.physicsWorkerNodes = {}; var physicsWorkerEdges = {}; for (let nodeId in nodes) { if (nodes.hasOwnProperty(nodeId)) { let node = nodes[nodeId]; if (node.options.physics === true) { - physicsWorkerNodes[nodeId] = { + // for updating fixed later + this.physicsBody.physicsNodeIndices.push(nodeId); + this.physicsWorkerNodes[nodeId] = { id: node.id, x: node.x, y: node.y, @@ -522,15 +529,11 @@ class PhysicsEngine { this.physicsWorker.postMessage({ type: 'physicsObjects', data: { - nodes: physicsWorkerNodes, + nodes: this.physicsWorkerNodes, edges: physicsWorkerEdges } }); } else { - this.physicsBody.forces = {}; - this.physicsBody.physicsNodeIndices = []; - this.physicsBody.physicsEdgeIndices = []; - // get node indices for physics for (let nodeId in nodes) { if (nodes.hasOwnProperty(nodeId)) { @@ -571,11 +574,11 @@ class PhysicsEngine { updateWorkerPositions() { if (this.physicsWorker) { - for(let i = 0; i < this.draggingNodes.length; i++) { + for (let i = 0; i < this.draggingNodes.length; i++) { let nodeId = this.draggingNodes[i]; let node = this.body.nodes[nodeId]; this.physicsWorker.postMessage({ - type: 'update', + type: 'updatePositions', data: { id: nodeId, x: node.x, @@ -586,6 +589,35 @@ class PhysicsEngine { } } + updateWorkerFixed() { + if (this.physicsWorker) { + for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { + let nodeId = this.physicsBody.physicsNodeIndices[i]; + let physicsNode = this.physicsWorkerNodes[nodeId]; + let node = this.body.nodes[nodeId]; + if (physicsNode.options.fixed.x !== node.options.fixed.x || + physicsNode.options.fixed.y !== node.options.fixed.y) + { + let fixed = { + x: node.options.fixed.x, + y: node.options.fixed.y + }; + physicsNode.options.fixed.x = fixed.x; + physicsNode.options.fixed.y = fixed.y; + this.physicsWorker.postMessage({ + type: 'updateFixed', + data: { + id: nodeId, + x: node.x, + y: node.y, + fixed: fixed + } + }); + } + } + } + } + /** * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. */ diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index 80ee0877..70994847 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -33,12 +33,19 @@ class PhysicsWorker { } }); break; - case 'update': - let node = this.body.nodes[msg.data.id]; - node.x = msg.data.x; - node.y = msg.data.y; - this.physicsBody.forces[node.id] = {x: 0, y: 0}; - this.physicsBody.velocities[node.id] = {x: 0, y: 0}; + case 'updatePositions': + let updatedNode = this.body.nodes[msg.data.id]; + updatedNode.x = msg.data.x; + updatedNode.y = msg.data.y; + this.physicsBody.forces[updatedNode.id] = {x: 0, y: 0}; + this.physicsBody.velocities[updatedNode.id] = {x: 0, y: 0}; + break; + case 'updateFixed': + let fixedNode = this.body.nodes[msg.data.id]; + fixedNode.x = msg.data.x; + fixedNode.y = msg.data.y; + fixedNode.options.fixed.x = msg.data.fixed.x; + fixedNode.options.fixed.y = msg.data.fixed.y; break; case 'options': this.options = msg.data; @@ -181,12 +188,12 @@ class PhysicsWorker { velocities[nodeId].x += ax * timestep; // velocity velocities[nodeId].x = (Math.abs(velocities[nodeId].x) > maxVelocity) ? ((velocities[nodeId].x > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].x; node.x += velocities[nodeId].x * timestep; // position - this.positions[nodeId].x = node.x; } else { forces[nodeId].x = 0; velocities[nodeId].x = 0; } + this.positions[nodeId].x = node.x; if (node.options.fixed.y === false) { let dy = this.modelOptions.damping * velocities[nodeId].y; // damping force @@ -194,12 +201,12 @@ class PhysicsWorker { velocities[nodeId].y += ay * timestep; // velocity velocities[nodeId].y = (Math.abs(velocities[nodeId].y) > maxVelocity) ? ((velocities[nodeId].y > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].y; node.y += velocities[nodeId].y * timestep; // position - this.positions[nodeId].y = node.y; } else { forces[nodeId].y = 0; velocities[nodeId].y = 0; } + this.positions[nodeId].y = node.y; let totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x,2) + Math.pow(velocities[nodeId].y,2)); return totalVelocity; From 545c894dd3671c405e6d2c0ad28e8b113a43b1ea Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Tue, 6 Oct 2015 18:01:25 -0400 Subject: [PATCH 07/17] bug fix for disconnected springs - all the physics solvers check for connected so only include those --- lib/network/modules/PhysicsEngine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index 1ad93783..ff13ccb0 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -498,7 +498,7 @@ class PhysicsEngine { for (let edgeId in edges) { if (edges.hasOwnProperty(edgeId)) { let edge = edges[edgeId]; - if (edge.options.physics === true) { + if (edge.options.physics === true && edge.connected === true) { physicsWorkerEdges[edgeId] = { connected: edge.connected, id: edge.id, From 0586985726e0f8051ead92a494df9dce9792ea96 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Wed, 7 Oct 2015 00:33:19 -0400 Subject: [PATCH 08/17] start on node changes based pushes to physicsWorker --- lib/network/modules/PhysicsEngine.js | 30 +++++++++-- lib/network/modules/PhysicsWorker.js | 52 +++++++++++++------ lib/network/modules/components/Node.js | 70 ++++++++++++++++++++++---- 3 files changed, 123 insertions(+), 29 deletions(-) diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index ff13ccb0..9f8fa20c 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -90,6 +90,8 @@ class PhysicsEngine { this.timestep = 0.5; this.layoutFailed = false; this.draggingNodes = []; + this.positionUpdateHandler = () => {}; + this.physicsUpdateHandler = () => {}; this.bindEventListeners(); } @@ -115,6 +117,8 @@ class PhysicsEngine { this.stopSimulation(false); this.body.emitter.off(); }); + this.body.emitter.on('_positionUpdate', (properties) => this.positionUpdateHandler(properties)); + this.body.emitter.on('_physicsUpdate', (properties) => this.physicsUpdateHandler(properties)); // For identifying which nodes to send to worker thread this.body.emitter.on('dragStart', (properties) => {this.draggingNodes = properties.nodes;}); this.body.emitter.on('dragEnd', () => { @@ -175,6 +179,8 @@ class PhysicsEngine { * configure the engine. */ initEmbeddedPhysics() { + this.positionUpdateHandler = () => {}; + this.physicsUpdateHandler = () => {}; if (this.physicsWorker) { this.options.useWorker = false; this.physicsWorker.terminate(); @@ -237,10 +243,24 @@ class PhysicsEngine { this.physicsWorkerMessageHandler(event); }); this.physicsWorker.onerror = (event) => { - console.error('Falling back to embedded physics engine'); + console.error('Falling back to embedded physics engine', event); this.initEmbeddedPhysics(); // throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")"); }; + this.positionUpdateHandler = (positions) => { + this.physicsWorker.postMessage({type: 'updatePositions', data: positions}); + }; + this.physicsUpdateHandler = (properties) => { + if (properties.options.physics !== undefined) { + if (properties.options.physics) { + this.physicsWorker.postMessage({type: 'addElements', data: 'TODO: createNode'}); + } else { + this.physicsWorker.postMessage({type: 'removeElements', data: properties.id}); + } + } else { + this.physicsWorker.postMessage({type: 'updateProperty', data: properties}); + } + }; } } @@ -259,8 +279,8 @@ class PhysicsEngine { let node = this.body.nodes[nodeId]; // handle case where we get a positions from an old physicsObject if (node) { - node.x = positions[nodeId].x; - node.y = positions[nodeId].y; + node.setX(positions[nodeId].x); + node.setY(positions[nodeId].y); } } break; @@ -719,7 +739,7 @@ class PhysicsEngine { let ax = (forces[nodeId].x - dx) / node.options.mass; // acceleration velocities[nodeId].x += ax * timestep; // velocity velocities[nodeId].x = (Math.abs(velocities[nodeId].x) > maxVelocity) ? ((velocities[nodeId].x > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].x; - node.x += velocities[nodeId].x * timestep; // position + node.setX(node.x + velocities[nodeId].x * timestep); // position } else { forces[nodeId].x = 0; @@ -731,7 +751,7 @@ class PhysicsEngine { let ay = (forces[nodeId].y - dy) / node.options.mass; // acceleration velocities[nodeId].y += ay * timestep; // velocity velocities[nodeId].y = (Math.abs(velocities[nodeId].y) > maxVelocity) ? ((velocities[nodeId].y > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].y; - node.y += velocities[nodeId].y * timestep; // position + node.setY(node.y + velocities[nodeId].y * timestep); // position } else { forces[nodeId].y = 0; diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index 70994847..197a95ad 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -9,7 +9,10 @@ import ForceAtlas2BasedCentralGravitySolver from './components/physics/FA2BasedC class PhysicsWorker { constructor(postMessage) { - this.body = {}; + this.body = { + nodes: {}, + edges: {} + }; this.physicsBody = {physicsNodeIndices:[], physicsEdgeIndices:[], forces: {}, velocities: {}}; this.postMessage = postMessage; this.options = {}; @@ -35,28 +38,47 @@ class PhysicsWorker { break; case 'updatePositions': let updatedNode = this.body.nodes[msg.data.id]; - updatedNode.x = msg.data.x; - updatedNode.y = msg.data.y; - this.physicsBody.forces[updatedNode.id] = {x: 0, y: 0}; - this.physicsBody.velocities[updatedNode.id] = {x: 0, y: 0}; + if (updatedNode) { + updatedNode.x = msg.data.x; + updatedNode.y = msg.data.y; + this.physicsBody.forces[updatedNode.id] = {x: 0, y: 0}; + this.physicsBody.velocities[updatedNode.id] = {x: 0, y: 0}; + } break; - case 'updateFixed': - let fixedNode = this.body.nodes[msg.data.id]; - fixedNode.x = msg.data.x; - fixedNode.y = msg.data.y; - fixedNode.options.fixed.x = msg.data.fixed.x; - fixedNode.options.fixed.y = msg.data.fixed.y; + case 'updateProperty': + let optionsNode = this.body.nodes[msg.data.id]; + if (optionsNode) { + let opts = msg.data.options; + // TODO see if we need a position with fixed + if (opts.fixed) { + if (opts.fixed.x !== undefined) { + optionsNode.options.fixed.x = opts.fixed.x; + } + if (opts.fixed.y !== undefined) { + optionsNode.options.fixed.y = opts.fixed.y; + } + } + if (opts.mass !== undefined) { + optionsNode.options.mass = opts.mass; + } + } break; - case 'options': - this.options = msg.data; - this.timestep = this.options.timestep; - this.init(); + case 'addElements': + console.log('add'); + break; + case 'removeElements': + console.log('remove'); break; case 'physicsObjects': this.body.nodes = msg.data.nodes; this.body.edges = msg.data.edges; this.updatePhysicsData(); break; + case 'options': + this.options = msg.data; + this.timestep = this.options.timestep; + this.init(); + break; default: console.warn('unknown message from PhysicsEngine', msg); } diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index 89152195..9e88e744 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -59,8 +59,8 @@ class Node { this.grouplist = grouplist; // state options - this.x = undefined; - this.y = undefined; + this._x = undefined; + this._y = undefined; this.baseSize = this.options.size; this.baseFontSize = this.options.font.size; this.predefinedPosition = false; // used to check if initial fit should just take the range or approximate @@ -71,6 +71,39 @@ class Node { this.setOptions(options); } + get x() { + return this._x; + } + + set x(newX) { + this._x = newX; + this.body.emitter.emit('_positionUpdate', {id: this.id, x: this._x, y: this._y}); + } + + /** + * Non emitting version for use by physics engine so we don't create infinite loops. + * @param newX + */ + setX(newX) { + this._x = newX; + } + + get y() { + return this._y; + } + + set y(newY) { + this._y = newY; + this.body.emitter.emit('_positionUpdate', {id: this.id, x: this._x, y: this._y}); + } + + /** + * Non emitting version for use by physics engine so we don't create infinite loops. + * @param newY + */ + setY(newY) { + this._y = newY; + } /** * Attach a edge to the node @@ -136,7 +169,7 @@ class Node { } // this transforms all shorthands into fully defined options - Node.parseOptions(this.options, options, true, this.globalOptions); + this.parseOptions(this.options, options, true, this.globalOptions); // load the images if (this.options.image !== undefined) { @@ -151,8 +184,16 @@ class Node { this.updateLabelModule(); this.updateShape(currentShape); + if (options.mass !== undefined) { + this.options.mass = options.mass; + this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {mass: options.mass}}); + } + if (options.physics !== undefined) { + this.options.physics = options.physics; + this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {physics: options.physics}}); + } - if (options.hidden !== undefined || options.physics !== undefined) { + if (options.hidden !== undefined) { return true; } return false; @@ -165,7 +206,7 @@ class Node { * @param parentOptions * @param newOptions */ - static parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) { + parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) { var fields = [ 'color', 'font', @@ -189,15 +230,26 @@ class Node { // handle the fixed options if (newOptions.fixed !== undefined && newOptions.fixed !== null) { if (typeof newOptions.fixed === 'boolean') { - parentOptions.fixed.x = newOptions.fixed; - parentOptions.fixed.y = newOptions.fixed; + if (parentOptions.fixed.x !== newOptions.fixed || parentOptions.fixed.y !== newOptions.fixed) { + parentOptions.fixed.x = newOptions.fixed; + parentOptions.fixed.y = newOptions.fixed; + this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {fixed: {x: newOptions.fixed, y: newOptions.fixed}}}); + } } else { - if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') { + if (newOptions.fixed.x !== undefined && + typeof newOptions.fixed.x === 'boolean' && + parentOptions.fixed.x !== newOptions.fixed.x) + { parentOptions.fixed.x = newOptions.fixed.x; + this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {fixed: {x: newOptions.fixed.x}}}); } - if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') { + if (newOptions.fixed.y !== undefined && + typeof newOptions.fixed.y === 'boolean' && + parentOptions.fixed.y !== newOptions.fixed.y) + { parentOptions.fixed.y = newOptions.fixed.y; + this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {fixed: {y: newOptions.fixed.y}}}); } } } From ea2a4951e58e44c3cd592331645b8654d2408ee9 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Wed, 7 Oct 2015 13:39:05 -0400 Subject: [PATCH 09/17] switch node changes over to events --- lib/network/Network.js | 2 +- lib/network/modules/InteractionHandler.js | 6 +- lib/network/modules/LayoutEngine.js | 4 +- lib/network/modules/PhysicsEngine.js | 128 +++++++------------- lib/network/modules/PhysicsWorker.js | 138 ++++++++++++++++------ lib/network/modules/components/Node.js | 33 ++++-- 6 files changed, 175 insertions(+), 136 deletions(-) diff --git a/lib/network/Network.js b/lib/network/Network.js index 7b3ae0e1..536876b3 100644 --- a/lib/network/Network.js +++ b/lib/network/Network.js @@ -270,7 +270,7 @@ Network.prototype.bindEventListeners = function () { this.body.emitter.on("_dataChanged", () => { // update shortcut lists this._updateVisibleIndices(); - this.physics.updatePhysicsData(); + this.physics.initPhysicsData(); this.body.emitter.emit("_requestRedraw"); // call the dataUpdated event because the only difference between the two is the updating of the indices this.body.emitter.emit("_dataUpdated"); diff --git a/lib/network/modules/InteractionHandler.js b/lib/network/modules/InteractionHandler.js index 76cc15cc..884912cd 100644 --- a/lib/network/modules/InteractionHandler.js +++ b/lib/network/modules/InteractionHandler.js @@ -313,8 +313,7 @@ class InteractionHandler { yFixed: object.options.fixed.y }; - object.options.fixed.x = true; - object.options.fixed.y = true; + object.setFixed(true); this.drag.selection.push(s); } @@ -395,8 +394,7 @@ class InteractionHandler { if (selection && selection.length) { selection.forEach(function (s) { // restore original xFixed and yFixed - s.node.options.fixed.x = s.xFixed; - s.node.options.fixed.y = s.yFixed; + s.node.setFixed({x: s.xFixed, y: s.yFixed}); }); this.selectionHandler._generateClickEvent('dragEnd', event, this.getPointer(event.center)); this.body.emitter.emit('startSimulation'); diff --git a/lib/network/modules/LayoutEngine.js b/lib/network/modules/LayoutEngine.js index b38bc393..d3d5e443 100644 --- a/lib/network/modules/LayoutEngine.js +++ b/lib/network/modules/LayoutEngine.js @@ -384,11 +384,11 @@ class LayoutEngine { let level = this.hierarchicalLevels[nodeId] === undefined ? 0 : this.hierarchicalLevels[nodeId]; if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { node.y = this.options.hierarchical.levelSeparation * level; - node.options.fixed.y = true; + node.setFixed({y: true}); } else { node.x = this.options.hierarchical.levelSeparation * level; - node.options.fixed.x = true; + node.setFixed({x: true}); } if (distribution[level] === undefined) { distribution[level] = {amount: 0, nodes: {}, distance: 0}; diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index 9f8fa20c..93065b8d 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -120,15 +120,13 @@ class PhysicsEngine { this.body.emitter.on('_positionUpdate', (properties) => this.positionUpdateHandler(properties)); this.body.emitter.on('_physicsUpdate', (properties) => this.physicsUpdateHandler(properties)); // For identifying which nodes to send to worker thread - this.body.emitter.on('dragStart', (properties) => {this.draggingNodes = properties.nodes;}); - this.body.emitter.on('dragEnd', () => { - // need one last update to handle the case where a drag happens - // and the user holds the node clicked at the final position - // for a time prior to releasing - this.updateWorkerPositions(); + this.body.emitter.on('dragStart', (properties) => { + this.draggingNodes = properties.nodes; + }); + this.body.emitter.on('dragEnd', () => { this.draggingNodes = []; }); - this.body.emitter.on('destroy', () => { + this.body.emitter.on('destroy', () => { if (this.physicsWorker) { this.physicsWorker.terminate(); this.physicsWorker = undefined; @@ -185,7 +183,7 @@ class PhysicsEngine { this.options.useWorker = false; this.physicsWorker.terminate(); this.physicsWorker = undefined; - this.updatePhysicsData(); + this.initPhysicsData(); } var options; if (this.options.solver === 'forceAtlas2Based') { @@ -253,12 +251,18 @@ class PhysicsEngine { this.physicsUpdateHandler = (properties) => { if (properties.options.physics !== undefined) { if (properties.options.physics) { - this.physicsWorker.postMessage({type: 'addElements', data: 'TODO: createNode'}); + this.physicsWorker.postMessage({ + type: 'addElements', + data: this.createPhysicsNode(properties.id) + }); } else { - this.physicsWorker.postMessage({type: 'removeElements', data: properties.id}); + this.physicsWorker.postMessage({type: 'removeElements', data: { + nodes: [properties.id.toString()], + edges: [] + }}); } } else { - this.physicsWorker.postMessage({type: 'updateProperty', data: properties}); + this.physicsWorker.postMessage({type: 'updateProperties', data: properties}); } }; } @@ -315,7 +319,6 @@ class PhysicsEngine { */ startSimulation() { if (this.physicsEnabled === true && this.options.enabled === true) { - this.updateWorkerPositions(); this.stabilized = false; // when visible, adaptivity is disabled. @@ -404,7 +407,6 @@ class PhysicsEngine { } if (this.stabilized === false) { - this.updateWorkerFixed(); // adaptivity means the timestep adapts to the situation, only applicable for stabilization if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) { // this is the factor for increasing the timestep on success. @@ -460,7 +462,7 @@ class PhysicsEngine { this.timestep = this.options.timestep; if (this.physicsWorker) { // console.log('asking working to do a physics iteration'); - this.physicsWorker.postMessage({type: 'calculateForces'}); + this.physicsWorker.postMessage({type: 'physicsTick'}); } else { this.calculateForces(); this.moveNodes(); @@ -476,12 +478,32 @@ class PhysicsEngine { } } + createPhysicsNode(nodeId) { + let node = this.body.nodes[nodeId]; + if (node && node.options.physics === true) { + // for updating fixed later + this.physicsBody.physicsNodeIndices.push(nodeId); + return { + id: node.id.toString(), + x: node.x, + y: node.y, + options: { + fixed: { + x: node.options.fixed.x, + y: node.options.fixed.y + }, + mass: node.options.mass + } + } + } + } + /** * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. * * @private */ - updatePhysicsData() { + initPhysicsData() { let nodes = this.body.nodes; let edges = this.body.edges; @@ -490,28 +512,12 @@ class PhysicsEngine { this.physicsBody.physicsEdgeIndices = []; if (this.physicsWorker) { - this.physicsWorkerNodes = {}; - var physicsWorkerEdges = {}; + let physicsWorkerNodes = {}; + let physicsWorkerEdges = {}; for (let nodeId in nodes) { if (nodes.hasOwnProperty(nodeId)) { - let node = nodes[nodeId]; - if (node.options.physics === true) { - // for updating fixed later - this.physicsBody.physicsNodeIndices.push(nodeId); - this.physicsWorkerNodes[nodeId] = { - id: node.id, - x: node.x, - y: node.y, - options: { - fixed: { - x: node.options.fixed.x, - y: node.options.fixed.y - }, - mass: node.options.mass - } - } - } + physicsWorkerNodes[nodeId] = this.createPhysicsNode(nodeId); } } @@ -549,7 +555,7 @@ class PhysicsEngine { this.physicsWorker.postMessage({ type: 'physicsObjects', data: { - nodes: this.physicsWorkerNodes, + nodes: physicsWorkerNodes, edges: physicsWorkerEdges } }); @@ -592,52 +598,6 @@ class PhysicsEngine { } } - updateWorkerPositions() { - if (this.physicsWorker) { - for (let i = 0; i < this.draggingNodes.length; i++) { - let nodeId = this.draggingNodes[i]; - let node = this.body.nodes[nodeId]; - this.physicsWorker.postMessage({ - type: 'updatePositions', - data: { - id: nodeId, - x: node.x, - y: node.y - } - }); - } - } - } - - updateWorkerFixed() { - if (this.physicsWorker) { - for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { - let nodeId = this.physicsBody.physicsNodeIndices[i]; - let physicsNode = this.physicsWorkerNodes[nodeId]; - let node = this.body.nodes[nodeId]; - if (physicsNode.options.fixed.x !== node.options.fixed.x || - physicsNode.options.fixed.y !== node.options.fixed.y) - { - let fixed = { - x: node.options.fixed.x, - y: node.options.fixed.y - }; - physicsNode.options.fixed.x = fixed.x; - physicsNode.options.fixed.y = fixed.y; - this.physicsWorker.postMessage({ - type: 'updateFixed', - data: { - id: nodeId, - x: node.x, - y: node.y, - fixed: fixed - } - }); - } - } - } - } - /** * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. */ @@ -786,8 +746,7 @@ class PhysicsEngine { if (nodes.hasOwnProperty(id)) { if (nodes[id].x && nodes[id].y) { this.freezeCache[id] = {x:nodes[id].options.fixed.x,y:nodes[id].options.fixed.y}; - nodes[id].options.fixed.x = true; - nodes[id].options.fixed.y = true; + nodes[id].setFixed(true); } } } @@ -803,8 +762,7 @@ class PhysicsEngine { for (var id in nodes) { if (nodes.hasOwnProperty(id)) { if (this.freezeCache[id] !== undefined) { - nodes[id].options.fixed.x = this.freezeCache[id].x; - nodes[id].options.fixed.y = this.freezeCache[id].y; + nodes[id].setFixed({x: this.freezeCache[id].x, y: this.freezeCache[id].y}); } } } diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index 197a95ad..d379d920 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -20,21 +20,17 @@ class PhysicsWorker { this.previousStates = {}; this.positions = {}; this.timestep = 0.5; + this.toRemove = { + nodes: [], + edges: [] + }; } handleMessage(event) { var msg = event.data; switch (msg.type) { - case 'calculateForces': - this.calculateForces(); - this.moveNodes(); - this.postMessage({ - type: 'positions', - data: { - positions: this.positions, - stabilized: this.stabilized - } - }); + case 'physicsTick': + this.physicsTick(); break; case 'updatePositions': let updatedNode = this.body.nodes[msg.data.id]; @@ -45,34 +41,22 @@ class PhysicsWorker { this.physicsBody.velocities[updatedNode.id] = {x: 0, y: 0}; } break; - case 'updateProperty': - let optionsNode = this.body.nodes[msg.data.id]; - if (optionsNode) { - let opts = msg.data.options; - // TODO see if we need a position with fixed - if (opts.fixed) { - if (opts.fixed.x !== undefined) { - optionsNode.options.fixed.x = opts.fixed.x; - } - if (opts.fixed.y !== undefined) { - optionsNode.options.fixed.y = opts.fixed.y; - } - } - if (opts.mass !== undefined) { - optionsNode.options.mass = opts.mass; - } - } + case 'updateProperties': + this.updateProperties(msg.data); break; case 'addElements': - console.log('add'); + this.addElements(msg.data); break; case 'removeElements': - console.log('remove'); + // schedule removal of elements on the next physicsTick + // avoids having to defensively check every node read in each physics implementation + this.toRemove.nodes.push.apply(this.toRemove.nodes, msg.data.nodes); + this.toRemove.edges.push.apply(this.toRemove.edges, msg.data.edges); break; case 'physicsObjects': this.body.nodes = msg.data.nodes; this.body.edges = msg.data.edges; - this.updatePhysicsData(); + this.initPhysicsData(); break; case 'options': this.options = msg.data; @@ -117,12 +101,92 @@ class PhysicsWorker { this.modelOptions = options; } + physicsTick() { + this.processRemovals(); + this.calculateForces(); + this.moveNodes(); + for (let i = 0; i < this.toRemove.nodes.length; i++) { + delete this.positions[this.toRemove.nodes[i]]; + } + this.postMessage({ + type: 'positions', + data: { + positions: this.positions, + stabilized: this.stabilized + } + }); + } + + updateProperties(data) { + let optionsNode = this.body.nodes[data.id]; + if (optionsNode) { + let opts = data.options; + if (opts.fixed) { + if (opts.fixed.x !== undefined) { + optionsNode.options.fixed.x = opts.fixed.x; + } + if (opts.fixed.y !== undefined) { + optionsNode.options.fixed.y = opts.fixed.y; + } + } + if (opts.mass !== undefined) { + optionsNode.options.mass = opts.mass; + } + } else { + console.log('sending property to unknown node'); + } + } + + addElements(data) { + // TODO expand to handle multiple and edges + let newNode = data; + let nodeId = newNode.id; + this.body.nodes[nodeId] = newNode; + this.positions[nodeId] = { + x: newNode.x, + y: newNode.y + }; + this.physicsBody.forces[nodeId] = {x: 0, y: 0}; + this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; + if (this.physicsBody.physicsNodeIndices.indexOf(nodeId) === -1) { + this.physicsBody.physicsNodeIndices.push(nodeId); + } + console.log('added node', nodeId); + } + + processRemovals() { + while (this.toRemove.nodes.length > 0) { + let nodeId = this.toRemove.nodes.pop(); + // TODO any optimization here? + let index = this.physicsBody.physicsNodeIndices.indexOf(nodeId); + if (index === -1 && typeof nodeId === 'number') { + index = this.physicsBody.physicsNodeIndices.indexOf(nodeId.toString()); + } + if (index > -1) { + this.physicsBody.physicsNodeIndices.splice(index,1); + } + delete this.physicsBody.forces[nodeId]; + delete this.physicsBody.velocities[nodeId]; + delete this.positions[nodeId]; + delete this.body.nodes[nodeId]; + console.log('removed node', nodeId); + } + while (this.toRemove.edges.length > 0) { + let edgeId = this.toRemove.edges.pop(); + let index = this.physicsBody.physicsEdgeIndices.indexOf(edgeId); + if (index > -1) { + this.physicsBody.physicsEdgeIndices.splice(index,1); + } + delete this.body.edges[edgeId]; + } + } + /** * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. * * @private */ - updatePhysicsData() { + initPhysicsData() { this.physicsBody.forces = {}; this.physicsBody.physicsNodeIndices = []; this.physicsBody.physicsEdgeIndices = []; @@ -132,18 +196,18 @@ class PhysicsWorker { // get node indices for physics for (let nodeId in nodes) { if (nodes.hasOwnProperty(nodeId)) { - this.physicsBody.physicsNodeIndices.push(nodeId); - this.positions[nodeId] = { - x: nodes[nodeId].x, - y: nodes[nodeId].y - } + this.physicsBody.physicsNodeIndices.push(nodeId); + this.positions[nodeId] = { + x: nodes[nodeId].x, + y: nodes[nodeId].y + } } } // get edge indices for physics for (let edgeId in edges) { if (edges.hasOwnProperty(edgeId)) { - this.physicsBody.physicsEdgeIndices.push(edgeId); + this.physicsBody.physicsEdgeIndices.push(edgeId); } } diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index 9e88e744..787520c9 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -97,6 +97,19 @@ class Node { this.body.emitter.emit('_positionUpdate', {id: this.id, x: this._x, y: this._y}); } + /** + * Emitting version + * + * @param newFixed + */ + setFixed(newFixed) { + // TODO split out fixed portion? + let physOpts = Node.parseOptions(this.options, {fixed: newFixed}); + if (Object.keys(physOpts).length > 0) { + this.body.emitter.emit('_physicsUpdate', {id: this.id, options: physOpts}); + } + } + /** * Non emitting version for use by physics engine so we don't create infinite loops. * @param newY @@ -169,7 +182,7 @@ class Node { } // this transforms all shorthands into fully defined options - this.parseOptions(this.options, options, true, this.globalOptions); + let physOpts = Node.parseOptions(this.options, options, true, this.globalOptions); // load the images if (this.options.image !== undefined) { @@ -186,11 +199,15 @@ class Node { if (options.mass !== undefined) { this.options.mass = options.mass; - this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {mass: options.mass}}); + physOpts.mass = options.mass; } if (options.physics !== undefined) { this.options.physics = options.physics; - this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {physics: options.physics}}); + physOpts.physics = options.physics; + } + + if (Object.keys(physOpts).length > 0) { + this.body.emitter.emit('_physicsUpdate', {id: this.id, options: physOpts}); } if (options.hidden !== undefined) { @@ -206,13 +223,14 @@ class Node { * @param parentOptions * @param newOptions */ - parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) { + static parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) { var fields = [ 'color', 'font', 'fixed', 'shadow' ]; + var changedPhysicsOptions = {}; util.selectiveNotDeepExtend(fields, parentOptions, newOptions, allowDeletion); // merge the shadow options into the parent. @@ -233,7 +251,7 @@ class Node { if (parentOptions.fixed.x !== newOptions.fixed || parentOptions.fixed.y !== newOptions.fixed) { parentOptions.fixed.x = newOptions.fixed; parentOptions.fixed.y = newOptions.fixed; - this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {fixed: {x: newOptions.fixed, y: newOptions.fixed}}}); + changedPhysicsOptions.fixed = {x: newOptions.fixed, y: newOptions.fixed}; } } else { @@ -242,14 +260,14 @@ class Node { parentOptions.fixed.x !== newOptions.fixed.x) { parentOptions.fixed.x = newOptions.fixed.x; - this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {fixed: {x: newOptions.fixed.x}}}); + util.deepExtend(changedPhysicsOptions, {fixed: {x: newOptions.fixed.x}}); } if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean' && parentOptions.fixed.y !== newOptions.fixed.y) { parentOptions.fixed.y = newOptions.fixed.y; - this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {fixed: {y: newOptions.fixed.y}}}); + util.deepExtend(changedPhysicsOptions, {fixed: {y: newOptions.fixed.y}}); } } } @@ -266,6 +284,7 @@ class Node { if (newOptions.scaling !== undefined) { util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label', allowDeletion, globalOptions.scaling); } + return changedPhysicsOptions; } updateLabelModule() { From 4469ba12a3be869fb75bcf2fc4de20965c29bf98 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Wed, 7 Oct 2015 15:50:42 -0400 Subject: [PATCH 10/17] update initial node creation --- lib/network/modules/PhysicsEngine.js | 150 +++++++++++++------------ lib/network/modules/PhysicsWorker.js | 94 +++++++--------- lib/network/modules/components/Node.js | 1 + 3 files changed, 115 insertions(+), 130 deletions(-) diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index 93065b8d..d4d7c6d9 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -251,9 +251,14 @@ class PhysicsEngine { this.physicsUpdateHandler = (properties) => { if (properties.options.physics !== undefined) { if (properties.options.physics) { + let nodes = {}; + nodes[properties.id] = this.createPhysicsNode(properties.id); this.physicsWorker.postMessage({ type: 'addElements', - data: this.createPhysicsNode(properties.id) + data: { + nodes: nodes, + edges: {} + } }); } else { this.physicsWorker.postMessage({type: 'removeElements', data: { @@ -480,9 +485,7 @@ class PhysicsEngine { createPhysicsNode(nodeId) { let node = this.body.nodes[nodeId]; - if (node && node.options.physics === true) { - // for updating fixed later - this.physicsBody.physicsNodeIndices.push(nodeId); + if (node) { return { id: node.id.toString(), x: node.x, @@ -498,6 +501,36 @@ class PhysicsEngine { } } + createPhysicsEdge(edgeId) { + let edge = this.body.edges[edgeId]; + if (edge && edge.options.physics === true && edge.connected === true) { + let physicsEdge = { + connected: edge.connected, + id: edge.id, + edgeType: {}, + toId: edge.toId, + fromId: edge.fromId, + to: { + id: edge.to.id + }, + from: { + id: edge.from.id + }, + options: { + length: edge.length + } + }; + if (edge.edgeType.via) { + physicsEdge.edgeType = { + via: { + id: edge.edgeType.via.id + } + } + } + return physicsEdge; + } + } + /** * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. * @@ -510,91 +543,60 @@ class PhysicsEngine { this.physicsBody.forces = {}; this.physicsBody.physicsNodeIndices = []; this.physicsBody.physicsEdgeIndices = []; + let physicsWorkerNodes = {}; + let physicsWorkerEdges = {}; - if (this.physicsWorker) { - let physicsWorkerNodes = {}; - let physicsWorkerEdges = {}; - for (let nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - physicsWorkerNodes[nodeId] = this.createPhysicsNode(nodeId); - } - } - - for (let edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - let edge = edges[edgeId]; - if (edge.options.physics === true && edge.connected === true) { - physicsWorkerEdges[edgeId] = { - connected: edge.connected, - id: edge.id, - edgeType: {}, - toId: edge.toId, - fromId: edge.fromId, - to: { - id: edge.to.id - }, - from: { - id: edge.from.id - }, - options: { - length: edge.length - } - }; - if (edge.edgeType.via) { - physicsWorkerEdges[edgeId].edgeType = { - via: { - id: edge.edgeType.via.id - } - } - } + // get node indices for physics + for (let nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + if (nodes[nodeId].options.physics === true) { + this.physicsBody.physicsNodeIndices.push(nodeId); + if (this.physicsWorker) { + physicsWorkerNodes[nodeId] = this.createPhysicsNode(nodeId); } } } + } - this.physicsWorker.postMessage({ - type: 'physicsObjects', - data: { - nodes: physicsWorkerNodes, - edges: physicsWorkerEdges - } - }); - } else { - // get node indices for physics - for (let nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].options.physics === true) { - this.physicsBody.physicsNodeIndices.push(nodeId); + // get edge indices for physics + for (let edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + if (edges[edgeId].options.physics === true) { + this.physicsBody.physicsEdgeIndices.push(edgeId); + if (this.physicsWorker) { + physicsWorkerEdges[edgeId] = this.createPhysicsEdge(edgeId); } } } + } - // get edge indices for physics - for (let edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].options.physics === true) { - this.physicsBody.physicsEdgeIndices.push(edgeId); - } - } - } + // get the velocity and the forces vector + for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { + let nodeId = this.physicsBody.physicsNodeIndices[i]; + this.physicsBody.forces[nodeId] = {x: 0, y: 0}; - // get the velocity and the forces vector - for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { - let nodeId = this.physicsBody.physicsNodeIndices[i]; - this.physicsBody.forces[nodeId] = {x: 0, y: 0}; + // forces can be reset because they are recalculated. Velocities have to persist. + if (this.physicsBody.velocities[nodeId] === undefined) { + this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; + } + } - // forces can be reset because they are recalculated. Velocities have to persist. - if (this.physicsBody.velocities[nodeId] === undefined) { - this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; - } + // clean deleted nodes from the velocity vector + for (let nodeId in this.physicsBody.velocities) { + if (nodes[nodeId] === undefined) { + delete this.physicsBody.velocities[nodeId]; } + } - // clean deleted nodes from the velocity vector - for (let nodeId in this.physicsBody.velocities) { - if (nodes[nodeId] === undefined) { - delete this.physicsBody.velocities[nodeId]; + if (this.physicsWorker) { + this.physicsWorker.postMessage({ + type: 'initPhysicsData', + data: { + nodes: physicsWorkerNodes, + edges: physicsWorkerEdges } - } + }); } } diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index d379d920..f0f5b868 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -53,10 +53,8 @@ class PhysicsWorker { this.toRemove.nodes.push.apply(this.toRemove.nodes, msg.data.nodes); this.toRemove.edges.push.apply(this.toRemove.edges, msg.data.edges); break; - case 'physicsObjects': - this.body.nodes = msg.data.nodes; - this.body.edges = msg.data.edges; - this.initPhysicsData(); + case 'initPhysicsData': + this.initPhysicsData(msg.data); break; case 'options': this.options = msg.data; @@ -137,31 +135,43 @@ class PhysicsWorker { } } - addElements(data) { - // TODO expand to handle multiple and edges - let newNode = data; - let nodeId = newNode.id; - this.body.nodes[nodeId] = newNode; - this.positions[nodeId] = { - x: newNode.x, - y: newNode.y - }; - this.physicsBody.forces[nodeId] = {x: 0, y: 0}; - this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; - if (this.physicsBody.physicsNodeIndices.indexOf(nodeId) === -1) { - this.physicsBody.physicsNodeIndices.push(nodeId); + addElements(data, replaceElements = true) { + let nodeIds = Object.keys(data.nodes); + for (let i = 0; i < nodeIds.length; i++) { + let nodeId = nodeIds[i]; + let newNode = data.nodes[nodeId]; + if (replaceElements) { + this.body.nodes[nodeId] = newNode; + } + this.positions[nodeId] = { + x: newNode.x, + y: newNode.y + }; + this.physicsBody.forces[nodeId] = {x: 0, y: 0}; + // forces can be reset because they are recalculated. Velocities have to persist. + if (this.physicsBody.velocities[nodeId] === undefined) { + this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; + } + if (this.physicsBody.physicsNodeIndices.indexOf(nodeId) === -1) { + this.physicsBody.physicsNodeIndices.push(nodeId); + } + } + let edgeIds = Object.keys(data.edges); + for (let i = 0; i < edgeIds.length; i++) { + let edgeId = edgeIds[i]; + if (replaceElements) { + this.body.edges[edgeId] = data.edges[edgeId]; + } + if (this.physicsBody.physicsEdgeIndices.indexOf(edgeId) === -1) { + this.physicsBody.physicsEdgeIndices.push(edgeId); + } } - console.log('added node', nodeId); } processRemovals() { while (this.toRemove.nodes.length > 0) { let nodeId = this.toRemove.nodes.pop(); - // TODO any optimization here? let index = this.physicsBody.physicsNodeIndices.indexOf(nodeId); - if (index === -1 && typeof nodeId === 'number') { - index = this.physicsBody.physicsNodeIndices.indexOf(nodeId.toString()); - } if (index > -1) { this.physicsBody.physicsNodeIndices.splice(index,1); } @@ -169,7 +179,6 @@ class PhysicsWorker { delete this.physicsBody.velocities[nodeId]; delete this.positions[nodeId]; delete this.body.nodes[nodeId]; - console.log('removed node', nodeId); } while (this.toRemove.edges.length > 0) { let edgeId = this.toRemove.edges.pop(); @@ -186,49 +195,22 @@ class PhysicsWorker { * * @private */ - initPhysicsData() { + initPhysicsData(data) { this.physicsBody.forces = {}; this.physicsBody.physicsNodeIndices = []; this.physicsBody.physicsEdgeIndices = []; - let nodes = this.body.nodes; - let edges = this.body.edges; - - // get node indices for physics - for (let nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - this.physicsBody.physicsNodeIndices.push(nodeId); - this.positions[nodeId] = { - x: nodes[nodeId].x, - y: nodes[nodeId].y - } - } - } - - // get edge indices for physics - for (let edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - this.physicsBody.physicsEdgeIndices.push(edgeId); - } - } - - // get the velocity and the forces vector - for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { - let nodeId = this.physicsBody.physicsNodeIndices[i]; - this.physicsBody.forces[nodeId] = {x: 0, y: 0}; + this.positions = {}; - // forces can be reset because they are recalculated. Velocities have to persist. - if (this.physicsBody.velocities[nodeId] === undefined) { - this.physicsBody.velocities[nodeId] = {x: 0, y: 0}; - } - } + this.body.nodes = data.nodes; + this.body.edges = data.edges; + this.addElements(data, false); // clean deleted nodes from the velocity vector for (let nodeId in this.physicsBody.velocities) { - if (nodes[nodeId] === undefined) { + if (this.body.nodes[nodeId] === undefined) { delete this.physicsBody.velocities[nodeId]; } } - // console.log(this.physicsBody); } /** diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index 787520c9..3c0d38a1 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -210,6 +210,7 @@ class Node { this.body.emitter.emit('_physicsUpdate', {id: this.id, options: physOpts}); } + // TODO make embedded physics trigger this or handle _physicsUpdate messages if (options.hidden !== undefined) { return true; } From 5bfa3859246c309b51ef07b2dc81b60f5a9b49b9 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Wed, 7 Oct 2015 16:47:16 -0400 Subject: [PATCH 11/17] initial functionality for edges - connected is working, others are TODOs --- lib/network/modules/PhysicsEngine.js | 40 +++++++++++++----- lib/network/modules/PhysicsWorker.js | 56 ++++++++++++++++---------- lib/network/modules/components/Edge.js | 13 ++++++ lib/network/modules/components/Node.js | 4 +- 4 files changed, 79 insertions(+), 34 deletions(-) diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index d4d7c6d9..5c18d42b 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -251,20 +251,34 @@ class PhysicsEngine { this.physicsUpdateHandler = (properties) => { if (properties.options.physics !== undefined) { if (properties.options.physics) { - let nodes = {}; - nodes[properties.id] = this.createPhysicsNode(properties.id); + let data = { + nodes: {}, + edges: {} + }; + if (properties.type === 'node') { + data.nodes[properties.id] = this.createPhysicsNode(properties.id); + } else if (properties.type === 'edge') { + data.edges[properties.id] = this.createPhysicsEdge(properties.id); + } else { + console.warn('invalid element type'); + } this.physicsWorker.postMessage({ type: 'addElements', - data: { - nodes: nodes, - edges: {} - } + data: data }); } else { - this.physicsWorker.postMessage({type: 'removeElements', data: { - nodes: [properties.id.toString()], - edges: [] - }}); + let data = { + nodeIds: [], + edgeIds: [] + }; + if (properties.type === 'node') { + data.nodeIds = [properties.id.toString()]; + } else if (properties.type === 'edge') { + data.edgeIds = [properties.id.toString()]; + } else { + console.warn('invalid element type'); + } + this.physicsWorker.postMessage({type: 'removeElements', data: data}); } } else { this.physicsWorker.postMessage({type: 'updateProperties', data: properties}); @@ -505,11 +519,14 @@ class PhysicsEngine { let edge = this.body.edges[edgeId]; if (edge && edge.options.physics === true && edge.connected === true) { let physicsEdge = { - connected: edge.connected, id: edge.id, + connected: edge.connected, edgeType: {}, toId: edge.toId, fromId: edge.fromId, + // TODO when will toId not match to.id given that connected is true + // can we rewrite SpringSolver to only use toId and not to.id + // and remove these parameters to: { id: edge.to.id }, @@ -520,6 +537,7 @@ class PhysicsEngine { length: edge.length } }; + // TODO test/implment dynamic if (edge.edgeType.via) { physicsEdge.edgeType = { via: { diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index f0f5b868..99586d71 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -21,8 +21,8 @@ class PhysicsWorker { this.positions = {}; this.timestep = 0.5; this.toRemove = { - nodes: [], - edges: [] + nodeIds: [], + edgeIds: [] }; } @@ -50,8 +50,8 @@ class PhysicsWorker { case 'removeElements': // schedule removal of elements on the next physicsTick // avoids having to defensively check every node read in each physics implementation - this.toRemove.nodes.push.apply(this.toRemove.nodes, msg.data.nodes); - this.toRemove.edges.push.apply(this.toRemove.edges, msg.data.edges); + this.toRemove.nodeIds.push.apply(this.toRemove.nodeIds, msg.data.nodeIds); + this.toRemove.edgeIds.push.apply(this.toRemove.edgeIds, msg.data.edgeIds); break; case 'initPhysicsData': this.initPhysicsData(msg.data); @@ -103,8 +103,8 @@ class PhysicsWorker { this.processRemovals(); this.calculateForces(); this.moveNodes(); - for (let i = 0; i < this.toRemove.nodes.length; i++) { - delete this.positions[this.toRemove.nodes[i]]; + for (let i = 0; i < this.toRemove.nodeIds.length; i++) { + delete this.positions[this.toRemove.nodeIds[i]]; } this.postMessage({ type: 'positions', @@ -116,22 +116,36 @@ class PhysicsWorker { } updateProperties(data) { - let optionsNode = this.body.nodes[data.id]; - if (optionsNode) { - let opts = data.options; - if (opts.fixed) { - if (opts.fixed.x !== undefined) { - optionsNode.options.fixed.x = opts.fixed.x; + if (data.type === 'node') { + let optionsNode = this.body.nodes[data.id]; + if (optionsNode) { + let opts = data.options; + if (opts.fixed) { + if (opts.fixed.x !== undefined) { + optionsNode.options.fixed.x = opts.fixed.x; + } + if (opts.fixed.y !== undefined) { + optionsNode.options.fixed.y = opts.fixed.y; + } } - if (opts.fixed.y !== undefined) { - optionsNode.options.fixed.y = opts.fixed.y; + if (opts.mass !== undefined) { + optionsNode.options.mass = opts.mass; } + } else { + console.warn('sending properties to unknown node'); } - if (opts.mass !== undefined) { - optionsNode.options.mass = opts.mass; + } else if (data.type === 'edge') { + let edge = this.body.edges[data.id]; + if (edge) { + let opts = data.options; + if (opts.connected) { + edge.connected = opts.connected; + } + } else { + console.warn('sending properties to unknown edge'); } } else { - console.log('sending property to unknown node'); + console.warn('sending properties to unknown element'); } } @@ -169,8 +183,8 @@ class PhysicsWorker { } processRemovals() { - while (this.toRemove.nodes.length > 0) { - let nodeId = this.toRemove.nodes.pop(); + while (this.toRemove.nodeIds.length > 0) { + let nodeId = this.toRemove.nodeIds.pop(); let index = this.physicsBody.physicsNodeIndices.indexOf(nodeId); if (index > -1) { this.physicsBody.physicsNodeIndices.splice(index,1); @@ -180,8 +194,8 @@ class PhysicsWorker { delete this.positions[nodeId]; delete this.body.nodes[nodeId]; } - while (this.toRemove.edges.length > 0) { - let edgeId = this.toRemove.edges.pop(); + while (this.toRemove.edgeIds.length > 0) { + let edgeId = this.toRemove.edgeIds.pop(); let index = this.physicsBody.physicsEdgeIndices.indexOf(edgeId); if (index > -1) { this.physicsBody.physicsEdgeIndices.splice(index,1); diff --git a/lib/network/modules/components/Edge.js b/lib/network/modules/components/Edge.js index 236e10de..2cd057cf 100644 --- a/lib/network/modules/components/Edge.js +++ b/lib/network/modules/components/Edge.js @@ -51,7 +51,10 @@ class Edge { this.labelModule = new Label(this.body, this.options); + // prevents sending connected messages on initial creation as it should be handled by added element + this.sendPhysicsUpdates = false; this.setOptions(options); + this.sendPhysicsUpdates = true; } @@ -86,10 +89,14 @@ class Edge { // A node is connected when it has a from and to node that both exist in the network.body.nodes. this.connect(); + // TODO make changing physics of 1 edge not trigger a complete rebuild of physics processing. if (options.hidden !== undefined || options.physics !== undefined) { dataChanged = true; } + // TODO if edgeType.via.id, toId, fromId, to.id, or from.id changed + // emit _physicsUpdate + return dataChanged; } @@ -252,6 +259,8 @@ class Edge { * Connect an edge to its nodes */ connect() { + let previousConnected = this.connected; + this.disconnect(); this.from = this.body.nodes[this.fromId] || undefined; @@ -272,6 +281,10 @@ class Edge { } this.edgeType.connect(); + + if (this.sendPhysicsUpdates && this.connected !== previousConnected) { + this.body.emitter.emit('_physicsUpdate', {type: 'edge', id: this.id, options: {connected: this.connected}}); + } } diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index 3c0d38a1..b4aaee8e 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -106,7 +106,7 @@ class Node { // TODO split out fixed portion? let physOpts = Node.parseOptions(this.options, {fixed: newFixed}); if (Object.keys(physOpts).length > 0) { - this.body.emitter.emit('_physicsUpdate', {id: this.id, options: physOpts}); + this.body.emitter.emit('_physicsUpdate', {type: 'node', id: this.id, options: physOpts}); } } @@ -207,7 +207,7 @@ class Node { } if (Object.keys(physOpts).length > 0) { - this.body.emitter.emit('_physicsUpdate', {id: this.id, options: physOpts}); + this.body.emitter.emit('_physicsUpdate', {type: 'node', id: this.id, options: physOpts}); } // TODO make embedded physics trigger this or handle _physicsUpdate messages From 387f7aa96ef8d12484238f3b48108b091bd12e6e Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Wed, 7 Oct 2015 19:37:45 -0400 Subject: [PATCH 12/17] prevent physics update messages before nodes are constructed additional debugging in PhysicsWorker made assumption in createPhysicsEdge bug fix for filtered data sets in NodesHandler --- lib/network/modules/NodesHandler.js | 4 ++-- lib/network/modules/PhysicsEngine.js | 9 ++++++--- lib/network/modules/PhysicsWorker.js | 6 +++--- lib/network/modules/components/Node.js | 5 ++++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/network/modules/NodesHandler.js b/lib/network/modules/NodesHandler.js index a35cfce1..6d639a18 100644 --- a/lib/network/modules/NodesHandler.js +++ b/lib/network/modules/NodesHandler.js @@ -353,10 +353,10 @@ class NodesHandler { var dataset = this.body.data.nodes.getDataSet(); for (let nodeId in dataset._data) { - if (dataset._data.hasOwnProperty(nodeId)) { + if (dataset._data.hasOwnProperty(nodeId) && this.body.nodes.hasOwnProperty(nodeId)) { let node = this.body.nodes[nodeId]; if (dataset._data[nodeId].x != Math.round(node.x) || dataset._data[nodeId].y != Math.round(node.y)) { - dataArray.push({ id: nodeId, x: Math.round(node.x), y: Math.round(node.y) }); + dataArray.push({id: nodeId, x: Math.round(node.x), y: Math.round(node.y)}); } } } diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index 5c18d42b..b8f0a6ee 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -517,7 +517,7 @@ class PhysicsEngine { createPhysicsEdge(edgeId) { let edge = this.body.edges[edgeId]; - if (edge && edge.options.physics === true && edge.connected === true) { + if (edge && edge.options.physics === true) { let physicsEdge = { id: edge.id, connected: edge.connected, @@ -528,10 +528,13 @@ class PhysicsEngine { // can we rewrite SpringSolver to only use toId and not to.id // and remove these parameters to: { - id: edge.to.id + // if assumption above is not valid TODO update to.id and from.id when updating connected + // id: edge.to.id + id: edge.toId }, from: { - id: edge.from.id + // id: edge.from.id + id: edge.fromId }, options: { length: edge.length diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index 99586d71..dc8fd72a 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -132,7 +132,7 @@ class PhysicsWorker { optionsNode.options.mass = opts.mass; } } else { - console.warn('sending properties to unknown node'); + console.warn('sending properties to unknown node', data.id, data.options); } } else if (data.type === 'edge') { let edge = this.body.edges[data.id]; @@ -142,10 +142,10 @@ class PhysicsWorker { edge.connected = opts.connected; } } else { - console.warn('sending properties to unknown edge'); + console.warn('sending properties to unknown edge', data.id, data.options); } } else { - console.warn('sending properties to unknown element'); + console.warn('sending properties to unknown element', data.id, data.options); } } diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index b4aaee8e..825508cd 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -68,7 +68,10 @@ class Node { this.hover = false; this.labelModule = new Label(this.body, this.options); + // prevents sending connected messages on initial creation as it should be handled by added element + this.sendPhysicsUpdates = false; this.setOptions(options); + this.sendPhysicsUpdates = true; } get x() { @@ -206,7 +209,7 @@ class Node { physOpts.physics = options.physics; } - if (Object.keys(physOpts).length > 0) { + if (this.sendPhysicsUpdates && Object.keys(physOpts).length > 0) { this.body.emitter.emit('_physicsUpdate', {type: 'node', id: this.id, options: physOpts}); } From 822235747e22e60ac0f5427544cd26364c665aa1 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Fri, 23 Oct 2015 09:36:16 -0400 Subject: [PATCH 13/17] replace to.id and from.id with toId and fromId --- lib/network/modules/PhysicsEngine.js | 12 ------------ .../modules/components/physics/SpringSolver.js | 6 +++--- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index b8f0a6ee..cc3fa775 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -524,18 +524,6 @@ class PhysicsEngine { edgeType: {}, toId: edge.toId, fromId: edge.fromId, - // TODO when will toId not match to.id given that connected is true - // can we rewrite SpringSolver to only use toId and not to.id - // and remove these parameters - to: { - // if assumption above is not valid TODO update to.id and from.id when updating connected - // id: edge.to.id - id: edge.toId - }, - from: { - // id: edge.from.id - id: edge.fromId - }, options: { length: edge.length } diff --git a/lib/network/modules/components/physics/SpringSolver.js b/lib/network/modules/components/physics/SpringSolver.js index ac10617d..b16069bb 100644 --- a/lib/network/modules/components/physics/SpringSolver.js +++ b/lib/network/modules/components/physics/SpringSolver.js @@ -29,9 +29,9 @@ class SpringSolver { if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) { if (edge.edgeType.via !== undefined) { edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; - node1 = nodes[edge.to.id]; + node1 = nodes[edge.toId]; node2 = nodes[edge.edgeType.via.id]; - node3 = nodes[edge.from.id]; + node3 = nodes[edge.fromId]; this._calculateSpringForce(node1, node2, 0.5 * edgeLength); this._calculateSpringForce(node2, node3, 0.5 * edgeLength); @@ -40,7 +40,7 @@ class SpringSolver { // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger. edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5: edge.options.length; - this._calculateSpringForce(nodes[edge.from.id], nodes[edge.to.id], edgeLength); + this._calculateSpringForce(nodes[edge.fromId], nodes[edge.toId], edgeLength); } } } From 86e58014204ce396b8438fd26287bae4ffd36a09 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Fri, 23 Oct 2015 15:02:21 -0400 Subject: [PATCH 14/17] refactor common code into PhysicsBase --- lib/network/modules/PhysicsBase.js | 255 +++++++++++++++++ lib/network/modules/PhysicsEngine.js | 401 +++++++-------------------- lib/network/modules/PhysicsWorker.js | 189 ++++++------- 3 files changed, 435 insertions(+), 410 deletions(-) create mode 100644 lib/network/modules/PhysicsBase.js diff --git a/lib/network/modules/PhysicsBase.js b/lib/network/modules/PhysicsBase.js new file mode 100644 index 00000000..748997e6 --- /dev/null +++ b/lib/network/modules/PhysicsBase.js @@ -0,0 +1,255 @@ +import BarnesHutSolver from './components/physics/BarnesHutSolver'; +import Repulsion from './components/physics/RepulsionSolver'; +import HierarchicalRepulsion from './components/physics/HierarchicalRepulsionSolver'; +import SpringSolver from './components/physics/SpringSolver'; +import HierarchicalSpringSolver from './components/physics/HierarchicalSpringSolver'; +import CentralGravitySolver from './components/physics/CentralGravitySolver'; +import ForceAtlas2BasedRepulsionSolver from './components/physics/FA2BasedRepulsionSolver'; +import ForceAtlas2BasedCentralGravitySolver from './components/physics/FA2BasedCentralGravitySolver'; + +class PhysicsBase { + constructor() { + this.physicsBody = {physicsNodeIndices:[], physicsEdgeIndices:[], forces: {}, velocities: {}}; + this.options = {}; + + this.referenceState = {}; + this.previousStates = {}; + + this.startedStabilization = false; + this.stabilized = false; + this.stabilizationIterations = 0; + this.timestep = 0.5; + } + + /** + * configure the engine. + */ + initPhysicsSolvers() { + var options; + if (this.options.solver === 'forceAtlas2Based') { + options = this.options.forceAtlas2Based; + this.nodesSolver = new ForceAtlas2BasedRepulsionSolver(this.body, this.physicsBody, options); + this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new ForceAtlas2BasedCentralGravitySolver(this.body, this.physicsBody, options); + } + else if (this.options.solver === 'repulsion') { + options = this.options.repulsion; + this.nodesSolver = new Repulsion(this.body, this.physicsBody, options); + this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); + } + else if (this.options.solver === 'hierarchicalRepulsion') { + options = this.options.hierarchicalRepulsion; + this.nodesSolver = new HierarchicalRepulsion(this.body, this.physicsBody, options); + this.edgesSolver = new HierarchicalSpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); + } + else { // barnesHut + options = this.options.barnesHut; + this.nodesSolver = new BarnesHutSolver(this.body, this.physicsBody, options); + this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); + this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); + } + + this.modelOptions = options; + } + + /** + * A single simulation step (or 'tick') in the physics simulation + * + * @private + */ + physicsTick() { + // this is here to ensure that there is no start event when the network is already stable. + if (this.startedStabilization === false) { + this.emit('startStabilizing'); + this.startedStabilization = true; + } + + if (this.stabilized === false) { + // adaptivity means the timestep adapts to the situation, only applicable for stabilization + if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) { + // this is the factor for increasing the timestep on success. + let factor = 1.2; + + // we assume the adaptive interval is + if (this.adaptiveCounter % this.adaptiveInterval === 0) { // we leave the timestep stable for "interval" iterations. + // first the big step and revert. Revert saves the reference state. + this.timestep = 2 * this.timestep; + this.calculateForces(); + this.moveNodes(); + this.revert(); + + // now the normal step. Since this is the last step, it is the more stable one and we will take this. + this.timestep = 0.5 * this.timestep; + + // since it's half the step, we do it twice. + this.calculateForces(); + this.moveNodes(); + this.calculateForces(); + this.moveNodes(); + + // we compare the two steps. if it is acceptable we double the step. + if (this._evaluateStepQuality() === true) { + this.timestep = factor * this.timestep; + } + else { + // if not, we decrease the step to a minimum of the options timestep. + // if the decreased timestep is smaller than the options step, we do not reset the counter + // we assume that the options timestep is stable enough. + if (this.timestep/factor < this.options.timestep) { + this.timestep = this.options.timestep; + } + else { + // if the timestep was larger than 2 times the option one we check the adaptivity again to ensure + // that large instabilities do not form. + this.adaptiveCounter = -1; // check again next iteration + this.timestep = Math.max(this.options.timestep, this.timestep/factor); + } + } + } + else { + // normal step, keeping timestep constant + this.calculateForces(); + this.moveNodes(); + } + + // increment the counter + this.adaptiveCounter += 1; + } + else { + // case for the static timestep, we reset it to the one in options and take a normal step. + this.timestep = this.options.timestep; + this.calculateForces(); + this.moveNodes(); + } + + // determine if the network has stabilzied + if (this.stabilized === true) { + this.revert(); + } + + this.stabilizationIterations++; + } + } + + /** + * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. + */ + revert() { + var nodeIds = Object.keys(this.previousStates); + var nodes = this.body.nodes; + var velocities = this.physicsBody.velocities; + this.referenceState = {}; + + for (let i = 0; i < nodeIds.length; i++) { + let nodeId = nodeIds[i]; + if (nodes[nodeId] !== undefined) { + if (this.isWorker || nodes[nodeId].options.physics === true) { + this.referenceState[nodeId] = { + positions: {x:nodes[nodeId].x, y:nodes[nodeId].y} + }; + velocities[nodeId].x = this.previousStates[nodeId].vx; + velocities[nodeId].y = this.previousStates[nodeId].vy; + nodes[nodeId].x = this.previousStates[nodeId].x; + nodes[nodeId].y = this.previousStates[nodeId].y; + } + } + else { + delete this.previousStates[nodeId]; + } + } + } + + /** + * This compares the reference state to the current state + */ + _evaluateStepQuality() { + let dx, dy, dpos; + let nodes = this.body.nodes; + let reference = this.referenceState; + let posThreshold = 0.3; + + for (let nodeId in this.referenceState) { + if (this.referenceState.hasOwnProperty(nodeId) && nodes[nodeId] !== undefined) { + dx = nodes[nodeId].x - reference[nodeId].positions.x; + dy = nodes[nodeId].y - reference[nodeId].positions.y; + + dpos = Math.sqrt(Math.pow(dx,2) + Math.pow(dy,2)) + + if (dpos > posThreshold) { + return false; + } + } + } + return true; + } + + /** + * move the nodes one timestap and check if they are stabilized + * @returns {boolean} + */ + moveNodes() { + var nodeIndices = this.physicsBody.physicsNodeIndices; + var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; + var maxNodeVelocity = 0; + var averageNodeVelocity = 0; + + // the velocity threshold (energy in the system) for the adaptivity toggle + var velocityAdaptiveThreshold = 5; + + for (let i = 0; i < nodeIndices.length; i++) { + let nodeId = nodeIndices[i]; + let nodeVelocity = this._performStep(nodeId, maxVelocity); + // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized + maxNodeVelocity = Math.max(maxNodeVelocity,nodeVelocity); + averageNodeVelocity += nodeVelocity; + } + + // evaluating the stabilized and adaptiveTimestepEnabled conditions + this.adaptiveTimestepEnabled = (averageNodeVelocity/nodeIndices.length) < velocityAdaptiveThreshold; + this.stabilized = maxNodeVelocity < this.options.minVelocity; + } + + // TODO consider moving _performStep in here + // right now Physics nodes don't have setX setY functions + // - maybe switch logic of setX and set x? + // - add functions to physics nodes - seems not desirable + + /** + * calculate the forces for one physics iteration. + */ + calculateForces() { + this.gravitySolver.solve(); + this.nodesSolver.solve(); + this.edgesSolver.solve(); + } + + /** + * One batch of stabilization + * @private + */ + _stabilizationBatch() { + // this is here to ensure that there is at least one start event. + if (this.startedStabilization === false) { + this.emit('startStabilizing'); + this.startedStabilization = true; + } + + var count = 0; + while (this.stabilized === false && count < this.options.stabilization.updateInterval && this.stabilizationIterations < this.targetIterations) { + this.physicsTick(); + count++; + } + + if (this.stabilized === false && this.stabilizationIterations < this.targetIterations) { + this.emit('stabilizationProgress', {iterations: this.stabilizationIterations, total: this.targetIterations}); + setTimeout(this._stabilizationBatch.bind(this),0); + } + else { + this._finalizeStabilization(); + } + } +} + +export default PhysicsBase; \ No newline at end of file diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index cc3fa775..9ddb7b75 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -1,26 +1,17 @@ -import BarnesHutSolver from './components/physics/BarnesHutSolver'; -import Repulsion from './components/physics/RepulsionSolver'; -import HierarchicalRepulsion from './components/physics/HierarchicalRepulsionSolver'; -import SpringSolver from './components/physics/SpringSolver'; -import HierarchicalSpringSolver from './components/physics/HierarchicalSpringSolver'; -import CentralGravitySolver from './components/physics/CentralGravitySolver'; -import ForceAtlas2BasedRepulsionSolver from './components/physics/FA2BasedRepulsionSolver'; -import ForceAtlas2BasedCentralGravitySolver from './components/physics/FA2BasedCentralGravitySolver'; +import PhysicsBase from './PhysicsBase'; import PhysicsWorker from 'worker!./PhysicsWorkerWrapper'; var util = require('../../util'); -class PhysicsEngine { +class PhysicsEngine extends PhysicsBase { constructor(body) { + super(); this.body = body; - this.physicsBody = {physicsNodeIndices:[], physicsEdgeIndices:[], forces: {}, velocities: {}}; this.physicsEnabled = true; this.simulationInterval = 1000 / 60; this.requiresTimeout = true; - this.previousStates = {}; - this.referenceState = {}; this.freezeCache = {}; this.renderTimer = undefined; @@ -30,13 +21,9 @@ class PhysicsEngine { this.adaptiveCounter = 0; this.adaptiveInterval = 3; - this.stabilized = false; - this.startedStabilization = false; - this.stabilizationIterations = 0; this.ready = false; // will be set to true if the stabilize // default options - this.options = {}; this.defaultOptions = { enabled: true, useWorker: false, @@ -87,11 +74,11 @@ class PhysicsEngine { adaptiveTimestep: true }; util.extend(this.options, this.defaultOptions); - this.timestep = 0.5; this.layoutFailed = false; this.draggingNodes = []; this.positionUpdateHandler = () => {}; this.physicsUpdateHandler = () => {}; + this.emit = this.body.emitter.emit; this.bindEventListeners(); } @@ -185,37 +172,12 @@ class PhysicsEngine { this.physicsWorker = undefined; this.initPhysicsData(); } - var options; - if (this.options.solver === 'forceAtlas2Based') { - options = this.options.forceAtlas2Based; - this.nodesSolver = new ForceAtlas2BasedRepulsionSolver(this.body, this.physicsBody, options); - this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new ForceAtlas2BasedCentralGravitySolver(this.body, this.physicsBody, options); - } - else if (this.options.solver === 'repulsion') { - options = this.options.repulsion; - this.nodesSolver = new Repulsion(this.body, this.physicsBody, options); - this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); - } - else if (this.options.solver === 'hierarchicalRepulsion') { - options = this.options.hierarchicalRepulsion; - this.nodesSolver = new HierarchicalRepulsion(this.body, this.physicsBody, options); - this.edgesSolver = new HierarchicalSpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); - } - else { // barnesHut - options = this.options.barnesHut; - this.nodesSolver = new BarnesHutSolver(this.body, this.physicsBody, options); - this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); - } - - this.modelOptions = options; + this.initPhysicsSolvers(); } initPhysicsWorker() { if (!this.physicsWorker) { + // setup path to webworker javascript file if (!__webpack_public_path__) { let parentScript = document.getElementById('visjs'); if (parentScript) { @@ -236,6 +198,7 @@ class PhysicsEngine { } } } + // launch webworker this.physicsWorker = new PhysicsWorker(); this.physicsWorker.addEventListener('message', (event) => { this.physicsWorkerMessageHandler(event); @@ -249,41 +212,45 @@ class PhysicsEngine { this.physicsWorker.postMessage({type: 'updatePositions', data: positions}); }; this.physicsUpdateHandler = (properties) => { - if (properties.options.physics !== undefined) { - if (properties.options.physics) { - let data = { - nodes: {}, - edges: {} - }; - if (properties.type === 'node') { - data.nodes[properties.id] = this.createPhysicsNode(properties.id); - } else if (properties.type === 'edge') { - data.edges[properties.id] = this.createPhysicsEdge(properties.id); - } else { - console.warn('invalid element type'); - } - this.physicsWorker.postMessage({ - type: 'addElements', - data: data - }); - } else { - let data = { - nodeIds: [], - edgeIds: [] - }; - if (properties.type === 'node') { - data.nodeIds = [properties.id.toString()]; - } else if (properties.type === 'edge') { - data.edgeIds = [properties.id.toString()]; - } else { - console.warn('invalid element type'); - } - this.physicsWorker.postMessage({type: 'removeElements', data: data}); - } + this._physicsUpdateHandler(properties); + }; + } + } + + _physicsUpdateHandler(properties) { + if (properties.options.physics !== undefined) { + if (properties.options.physics) { + let data = { + nodes: {}, + edges: {} + }; + if (properties.type === 'node') { + data.nodes[properties.id] = this.createPhysicsNode(properties.id); + } else if (properties.type === 'edge') { + data.edges[properties.id] = this.createPhysicsEdge(properties.id); } else { - this.physicsWorker.postMessage({type: 'updateProperties', data: properties}); + console.warn('invalid element type'); } - }; + this.physicsWorker.postMessage({ + type: 'addElements', + data: data + }); + } else { + let data = { + nodeIds: [], + edgeIds: [] + }; + if (properties.type === 'node') { + data.nodeIds = [properties.id.toString()]; + } else if (properties.type === 'edge') { + data.edgeIds = [properties.id.toString()]; + } else { + console.warn('invalid element type'); + } + this.physicsWorker.postMessage({type: 'removeElements', data: data}); + } + } else { + this.physicsWorker.postMessage({type: 'updateProperties', data: properties}); } } @@ -292,26 +259,36 @@ class PhysicsEngine { switch (msg.type) { case 'positions': this.stabilized = msg.data.stabilized; - var positions = msg.data.positions; - for (let i = 0; i < this.draggingNodes.length; i++) { - delete positions[this.draggingNodes[i]]; - } - let nodeIds = Object.keys(positions); - for (let i = 0; i < nodeIds.length; i++) { - let nodeId = nodeIds[i]; - let node = this.body.nodes[nodeId]; - // handle case where we get a positions from an old physicsObject - if (node) { - node.setX(positions[nodeId].x); - node.setY(positions[nodeId].y); - } - } + this._receivedPositions(msg.data.positions); + break; + case 'finalizeStabilization': + this.stabilizationIterations = msg.data.stabilizationIterations; + this._finalizeStabilization(); + break; + case 'emit': + this.emit(msg.data.event, msg.data.data); break; default: console.warn('unhandled physics worker message:', msg); } } + _receivedPositions(positions) { + for (let i = 0; i < this.draggingNodes.length; i++) { + delete positions[this.draggingNodes[i]]; + } + let nodeIds = Object.keys(positions); + for (let i = 0; i < nodeIds.length; i++) { + let nodeId = nodeIds[i]; + let node = this.body.nodes[nodeId]; + // handle case where we get a positions from an old physicsObject + if (node) { + node.setX(positions[nodeId].x); + node.setY(positions[nodeId].y); + } + } + } + /** * initialize the engine */ @@ -380,17 +357,21 @@ class PhysicsEngine { * */ simulationStep() { - // check if the physics have settled - var startTime = Date.now(); - this.physicsTick(); - var physicsTime = Date.now() - startTime; - - // run double speed if it is a little graph - if ((physicsTime < 0.4 * this.simulationInterval || this.runDoubleSpeed === true) && this.stabilized === false) { + if (this.physicsWorker) { + this.physicsWorker.postMessage({type: 'physicsTick'}); + } else { + // check if the physics have settled + var startTime = Date.now(); this.physicsTick(); + var physicsTime = Date.now() - startTime; + + // run double speed if it is a little graph + if ((physicsTime < 0.4 * this.simulationInterval || this.runDoubleSpeed === true) && this.stabilized === false) { + this.physicsTick(); - // this makes sure there is no jitter. The decision is taken once to run it at double speed. - this.runDoubleSpeed = true; + // this makes sure there is no jitter. The decision is taken once to run it at double speed. + this.runDoubleSpeed = true; + } } if (this.stabilized === true) { @@ -398,7 +379,7 @@ class PhysicsEngine { } } - + // TODO determine when startedStabilization needs to be propogated from the worker /** * trigger the stabilized event. * @private @@ -413,90 +394,6 @@ class PhysicsEngine { } } - /** - * A single simulation step (or 'tick') in the physics simulation - * - * @private - */ - physicsTick() { - // this is here to ensure that there is no start event when the network is already stable. - if (this.startedStabilization === false) { - this.body.emitter.emit('startStabilizing'); - this.startedStabilization = true; - } - - if (this.stabilized === false) { - // adaptivity means the timestep adapts to the situation, only applicable for stabilization - if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) { - // this is the factor for increasing the timestep on success. - let factor = 1.2; - - // we assume the adaptive interval is - if (this.adaptiveCounter % this.adaptiveInterval === 0) { // we leave the timestep stable for "interval" iterations. - // first the big step and revert. Revert saves the reference state. - this.timestep = 2 * this.timestep; - this.calculateForces(); - this.moveNodes(); - this.revert(); - - // now the normal step. Since this is the last step, it is the more stable one and we will take this. - this.timestep = 0.5 * this.timestep; - - // since it's half the step, we do it twice. - this.calculateForces(); - this.moveNodes(); - this.calculateForces(); - this.moveNodes(); - - // we compare the two steps. if it is acceptable we double the step. - if (this._evaluateStepQuality() === true) { - this.timestep = factor * this.timestep; - } - else { - // if not, we decrease the step to a minimum of the options timestep. - // if the decreased timestep is smaller than the options step, we do not reset the counter - // we assume that the options timestep is stable enough. - if (this.timestep/factor < this.options.timestep) { - this.timestep = this.options.timestep; - } - else { - // if the timestep was larger than 2 times the option one we check the adaptivity again to ensure - // that large instabilities do not form. - this.adaptiveCounter = -1; // check again next iteration - this.timestep = Math.max(this.options.timestep, this.timestep/factor); - } - } - } - else { - // normal step, keeping timestep constant - this.calculateForces(); - this.moveNodes(); - } - - // increment the counter - this.adaptiveCounter += 1; - } - else { - // case for the static timestep, we reset it to the one in options and take a normal step. - this.timestep = this.options.timestep; - if (this.physicsWorker) { - // console.log('asking working to do a physics iteration'); - this.physicsWorker.postMessage({type: 'physicsTick'}); - } else { - this.calculateForces(); - this.moveNodes(); - } - } - - // determine if the network has stabilzied - if (this.stabilized === true) { - this.revert(); - } - - this.stabilizationIterations++; - } - } - createPhysicsNode(nodeId) { let node = this.body.nodes[nodeId]; if (node) { @@ -504,6 +401,10 @@ class PhysicsEngine { id: node.id.toString(), x: node.x, y: node.y, + // TODO update on change + edges: { + length: node.edges.length + }, options: { fixed: { x: node.options.fixed.x, @@ -609,85 +510,6 @@ class PhysicsEngine { } } - /** - * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. - */ - revert() { - var nodeIds = Object.keys(this.previousStates); - var nodes = this.body.nodes; - var velocities = this.physicsBody.velocities; - this.referenceState = {}; - - for (let i = 0; i < nodeIds.length; i++) { - let nodeId = nodeIds[i]; - if (nodes[nodeId] !== undefined) { - if (nodes[nodeId].options.physics === true) { - this.referenceState[nodeId] = { - positions: {x:nodes[nodeId].x, y:nodes[nodeId].y} - }; - velocities[nodeId].x = this.previousStates[nodeId].vx; - velocities[nodeId].y = this.previousStates[nodeId].vy; - nodes[nodeId].x = this.previousStates[nodeId].x; - nodes[nodeId].y = this.previousStates[nodeId].y; - } - } - else { - delete this.previousStates[nodeId]; - } - } - } - - /** - * This compares the reference state to the current state - */ - _evaluateStepQuality() { - let dx, dy, dpos; - let nodes = this.body.nodes; - let reference = this.referenceState; - let posThreshold = 0.3; - - for (let nodeId in this.referenceState) { - if (this.referenceState.hasOwnProperty(nodeId) && nodes[nodeId] !== undefined) { - dx = nodes[nodeId].x - reference[nodeId].positions.x; - dy = nodes[nodeId].y - reference[nodeId].positions.y; - - dpos = Math.sqrt(Math.pow(dx,2) + Math.pow(dy,2)) - - if (dpos > posThreshold) { - return false; - } - } - } - return true; - } - - /** - * move the nodes one timestap and check if they are stabilized - * @returns {boolean} - */ - moveNodes() { - var nodeIndices = this.physicsBody.physicsNodeIndices; - var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; - var maxNodeVelocity = 0; - var averageNodeVelocity = 0; - - // the velocity threshold (energy in the system) for the adaptivity toggle - var velocityAdaptiveThreshold = 5; - - for (let i = 0; i < nodeIndices.length; i++) { - let nodeId = nodeIndices[i]; - let nodeVelocity = this._performStep(nodeId, maxVelocity); - // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized - maxNodeVelocity = Math.max(maxNodeVelocity,nodeVelocity); - averageNodeVelocity += nodeVelocity; - } - - // evaluating the stabilized and adaptiveTimestepEnabled conditions - this.adaptiveTimestepEnabled = (averageNodeVelocity/nodeIndices.length) < velocityAdaptiveThreshold; - this.stabilized = maxNodeVelocity < this.options.minVelocity; - } - - /** * Perform the actual step * @@ -733,18 +555,6 @@ class PhysicsEngine { return totalVelocity; } - - /** - * calculate the forces for one physics iteration. - */ - calculateForces() { - this.gravitySolver.solve(); - this.nodesSolver.solve(); - this.edgesSolver.solve(); - } - - - /** * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization * because only the supportnodes for the smoothCurves have to settle. @@ -804,7 +614,7 @@ class PhysicsEngine { // stop the render loop this.stopSimulation(); - // set stabilze to false + // set stabilize to false this.stabilized = false; // block redraw requests @@ -817,37 +627,18 @@ class PhysicsEngine { } this.stabilizationIterations = 0; - setTimeout(() => this._stabilizationBatch(),0); - } - - - /** - * One batch of stabilization - * @private - */ - _stabilizationBatch() { - // this is here to ensure that there is at least one start event. - if (this.startedStabilization === false) { - this.body.emitter.emit('startStabilizing'); - this.startedStabilization = true; - } - - var count = 0; - while (this.stabilized === false && count < this.options.stabilization.updateInterval && this.stabilizationIterations < this.targetIterations) { - this.physicsTick(); - count++; - } - - if (this.stabilized === false && this.stabilizationIterations < this.targetIterations) { - this.body.emitter.emit('stabilizationProgress', {iterations: this.stabilizationIterations, total: this.targetIterations}); - setTimeout(this._stabilizationBatch.bind(this),0); - } - else { - this._finalizeStabilization(); + if (this.physicsWorker) { + this.physicsWorker.postMessage({ + type: 'stabilization', + data: { + targetIterations: iterations + } + }); + } else { + setTimeout(() => this._stabilizationBatch(), 0); } } - /** * Wrap up the stabilization, fit and emit the events. * @private diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index dc8fd72a..d21812ca 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -1,29 +1,21 @@ -import BarnesHutSolver from './components/physics/BarnesHutSolver'; -import Repulsion from './components/physics/RepulsionSolver'; -import HierarchicalRepulsion from './components/physics/HierarchicalRepulsionSolver'; -import SpringSolver from './components/physics/SpringSolver'; -import HierarchicalSpringSolver from './components/physics/HierarchicalSpringSolver'; -import CentralGravitySolver from './components/physics/CentralGravitySolver'; -import ForceAtlas2BasedRepulsionSolver from './components/physics/FA2BasedRepulsionSolver'; -import ForceAtlas2BasedCentralGravitySolver from './components/physics/FA2BasedCentralGravitySolver'; +import PhysicsBase from './PhysicsBase'; -class PhysicsWorker { +class PhysicsWorker extends PhysicsBase { constructor(postMessage) { + super(); this.body = { nodes: {}, edges: {} }; - this.physicsBody = {physicsNodeIndices:[], physicsEdgeIndices:[], forces: {}, velocities: {}}; this.postMessage = postMessage; - this.options = {}; - this.stabilized = false; this.previousStates = {}; - this.positions = {}; - this.timestep = 0.5; this.toRemove = { nodeIds: [], edgeIds: [] }; + this.physicsTimeout = null; + this.isWorker = true; + this.emit = (event, data) => {this.postMessage({type: 'emit', data: {event: event, data: data}})}; } handleMessage(event) { @@ -31,15 +23,10 @@ class PhysicsWorker { switch (msg.type) { case 'physicsTick': this.physicsTick(); + this.sendPositions(); break; case 'updatePositions': - let updatedNode = this.body.nodes[msg.data.id]; - if (updatedNode) { - updatedNode.x = msg.data.x; - updatedNode.y = msg.data.y; - this.physicsBody.forces[updatedNode.id] = {x: 0, y: 0}; - this.physicsBody.velocities[updatedNode.id] = {x: 0, y: 0}; - } + this.receivePositions(msg.data); break; case 'updateProperties': this.updateProperties(msg.data); @@ -48,73 +35,78 @@ class PhysicsWorker { this.addElements(msg.data); break; case 'removeElements': - // schedule removal of elements on the next physicsTick - // avoids having to defensively check every node read in each physics implementation - this.toRemove.nodeIds.push.apply(this.toRemove.nodeIds, msg.data.nodeIds); - this.toRemove.edgeIds.push.apply(this.toRemove.edgeIds, msg.data.edgeIds); + this.removeElements(msg.data); + break; + case 'stabilization': + this.stabilize(msg.data); break; case 'initPhysicsData': + console.debug('init physics data'); this.initPhysicsData(msg.data); break; case 'options': this.options = msg.data; this.timestep = this.options.timestep; - this.init(); + this.initPhysicsSolvers(); break; default: console.warn('unknown message from PhysicsEngine', msg); } } - /** - * configure the engine. - */ - init() { - var options; - if (this.options.solver === 'forceAtlas2Based') { - options = this.options.forceAtlas2Based; - this.nodesSolver = new ForceAtlas2BasedRepulsionSolver(this.body, this.physicsBody, options); - this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new ForceAtlas2BasedCentralGravitySolver(this.body, this.physicsBody, options); - } - else if (this.options.solver === 'repulsion') { - options = this.options.repulsion; - this.nodesSolver = new Repulsion(this.body, this.physicsBody, options); - this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); - } - else if (this.options.solver === 'hierarchicalRepulsion') { - options = this.options.hierarchicalRepulsion; - this.nodesSolver = new HierarchicalRepulsion(this.body, this.physicsBody, options); - this.edgesSolver = new HierarchicalSpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); - } - else { // barnesHut - options = this.options.barnesHut; - this.nodesSolver = new BarnesHutSolver(this.body, this.physicsBody, options); - this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); - this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); - } - - this.modelOptions = options; - } +// physicsTick() { +// if(this.physicsTimeout) { +// // cancel any outstanding requests to prevent manipulation of data while iterating +// // and we're going to handle it next anyways. +// clearTimeout(this.physicsTimeout); +// } +// this.processRemovals(); +// if (this.options.enabled) { +// this.calculateForces(); +// this.moveNodes(); +// // Handle the case where physics was enabled, data was removed +// // but this physics tick was longer than the timeout and during that delta +// // physics was disabled. +// this.processRemovals(); +// } +// this.stabilizationIterations++; +// } - physicsTick() { - this.processRemovals(); - this.calculateForces(); - this.moveNodes(); - for (let i = 0; i < this.toRemove.nodeIds.length; i++) { - delete this.positions[this.toRemove.nodeIds[i]]; + sendPositions() { + let nodeIndices = this.physicsBody.physicsNodeIndices; + let positions = {}; + for (let i = 0; i < nodeIndices.length; i++) { + let nodeId = nodeIndices[i]; + let node = this.body.nodes[nodeId]; + positions[nodeId] = {x:node.x, y:node.y}; } + this.postMessage({ type: 'positions', data: { - positions: this.positions, + positions: positions, stabilized: this.stabilized } }); } + receivePositions(data) { + let updatedNode = this.body.nodes[data.id]; + if (updatedNode) { + updatedNode.x = data.x; + updatedNode.y = data.y; + this.physicsBody.forces[updatedNode.id] = {x: 0, y: 0}; + this.physicsBody.velocities[updatedNode.id] = {x: 0, y: 0}; + } + } + + stabilize(data) { + this.stabilized = false; + this.targetIterations = data.targetIterations; + this.stabilizationIterations = 0; + setTimeout(() => this._stabilizationBatch(), 0); + } + updateProperties(data) { if (data.type === 'node') { let optionsNode = this.body.nodes[data.id]; @@ -157,10 +149,6 @@ class PhysicsWorker { if (replaceElements) { this.body.nodes[nodeId] = newNode; } - this.positions[nodeId] = { - x: newNode.x, - y: newNode.y - }; this.physicsBody.forces[nodeId] = {x: 0, y: 0}; // forces can be reset because they are recalculated. Velocities have to persist. if (this.physicsBody.velocities[nodeId] === undefined) { @@ -182,6 +170,25 @@ class PhysicsWorker { } } + removeElements(data) { + // schedule removal of elements on the next physicsTick + // avoids having to defensively check every node read in each physics implementation + this.toRemove.nodeIds.push.apply(this.toRemove.nodeIds, data.nodeIds); + this.toRemove.edgeIds.push.apply(this.toRemove.edgeIds, data.edgeIds); + // Handle case where physics is disabled. + if(this.physicsTimeout) { + // don't schedule more than one physicsTick + clearTimeout(this.physicsTimeout); + } + this.physicsTimeout = setTimeout(()=> { + // if physics is still enabled, the next tick will handle removeElements + if (!this.options.enabled) { + this.physicsTimeout = null; + this.physicsTick(); + } + }, 250); + } + processRemovals() { while (this.toRemove.nodeIds.length > 0) { let nodeId = this.toRemove.nodeIds.pop(); @@ -191,7 +198,6 @@ class PhysicsWorker { } delete this.physicsBody.forces[nodeId]; delete this.physicsBody.velocities[nodeId]; - delete this.positions[nodeId]; delete this.body.nodes[nodeId]; } while (this.toRemove.edgeIds.length > 0) { @@ -204,16 +210,10 @@ class PhysicsWorker { } } - /** - * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. - * - * @private - */ initPhysicsData(data) { this.physicsBody.forces = {}; this.physicsBody.physicsNodeIndices = []; this.physicsBody.physicsEdgeIndices = []; - this.positions = {}; this.body.nodes = data.nodes; this.body.edges = data.edges; @@ -227,26 +227,6 @@ class PhysicsWorker { } } - /** - * move the nodes one timestap and check if they are stabilized - * @returns {boolean} - */ - moveNodes() { - var nodeIndices = this.physicsBody.physicsNodeIndices; - var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; - var maxNodeVelocity = 0; - - for (let i = 0; i < nodeIndices.length; i++) { - let nodeId = nodeIndices[i]; - let nodeVelocity = this._performStep(nodeId, maxVelocity); - // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized - maxNodeVelocity = Math.max(maxNodeVelocity,nodeVelocity); - } - - // evaluating the stabilized and adaptiveTimestepEnabled conditions - this.stabilized = maxNodeVelocity < this.options.minVelocity; - } - /** * Perform the actual step * @@ -275,7 +255,6 @@ class PhysicsWorker { forces[nodeId].x = 0; velocities[nodeId].x = 0; } - this.positions[nodeId].x = node.x; if (node.options.fixed.y === false) { let dy = this.modelOptions.damping * velocities[nodeId].y; // damping force @@ -288,19 +267,19 @@ class PhysicsWorker { forces[nodeId].y = 0; velocities[nodeId].y = 0; } - this.positions[nodeId].y = node.y; let totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x,2) + Math.pow(velocities[nodeId].y,2)); return totalVelocity; } - /** - * calculate the forces for one physics iteration. - */ - calculateForces() { - this.gravitySolver.solve(); - this.nodesSolver.solve(); - this.edgesSolver.solve(); + _finalizeStabilization() { + this.sendPositions(); + this.postMessage({ + type: 'finalizeStabilization', + data: { + stabilizationIterations: this.stabilizationIterations + } + }); } } From c687e414dd08bdc3d1de0037e5116374316e341d Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Mon, 26 Oct 2015 09:44:33 -0400 Subject: [PATCH 15/17] final touches on stabilization --- lib/network/modules/PhysicsBase.js | 6 ++++++ lib/network/modules/PhysicsEngine.js | 21 +++++++++++++-------- lib/network/modules/PhysicsWorker.js | 8 +++++++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/network/modules/PhysicsBase.js b/lib/network/modules/PhysicsBase.js index 748997e6..37db6e07 100644 --- a/lib/network/modules/PhysicsBase.js +++ b/lib/network/modules/PhysicsBase.js @@ -19,6 +19,12 @@ class PhysicsBase { this.stabilized = false; this.stabilizationIterations = 0; this.timestep = 0.5; + + // parameters for the adaptive timestep + this.adaptiveTimestep = false; + this.adaptiveTimestepEnabled = false; + this.adaptiveCounter = 0; + this.adaptiveInterval = 3; } /** diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index 9ddb7b75..e217727b 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -15,12 +15,6 @@ class PhysicsEngine extends PhysicsBase { this.freezeCache = {}; this.renderTimer = undefined; - // parameters for the adaptive timestep - this.adaptiveTimestep = false; - this.adaptiveTimestepEnabled = false; - this.adaptiveCounter = 0; - this.adaptiveInterval = 3; - this.ready = false; // will be set to true if the stabilize // default options @@ -106,7 +100,6 @@ class PhysicsEngine extends PhysicsBase { }); this.body.emitter.on('_positionUpdate', (properties) => this.positionUpdateHandler(properties)); this.body.emitter.on('_physicsUpdate', (properties) => this.physicsUpdateHandler(properties)); - // For identifying which nodes to send to worker thread this.body.emitter.on('dragStart', (properties) => { this.draggingNodes = properties.nodes; }); @@ -316,6 +309,7 @@ class PhysicsEngine extends PhysicsBase { startSimulation() { if (this.physicsEnabled === true && this.options.enabled === true) { this.stabilized = false; + this._updateWorkerStabilized(); // when visible, adaptivity is disabled. this.adaptiveTimestep = false; @@ -339,6 +333,8 @@ class PhysicsEngine extends PhysicsBase { */ stopSimulation(emit = true) { this.stabilized = true; + this._updateWorkerStabilized(); + if (emit === true) { this._emitStabilized(); } @@ -351,6 +347,14 @@ class PhysicsEngine extends PhysicsBase { } } + _updateWorkerStabilized() { + if (this.physicsWorker) { + this.physicsWorker.postMessage({ + type: 'setStabilized', + data: this.stabilized + }); + } + } /** * The viewFunction inserts this step into each renderloop. It calls the physics tick and handles the cleanup at stabilized. @@ -555,6 +559,7 @@ class PhysicsEngine extends PhysicsBase { return totalVelocity; } + // TODO probably want to move freeze/restore to PhysicsBase and do in worker if running /** * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization * because only the supportnodes for the smoothCurves have to settle. @@ -629,7 +634,7 @@ class PhysicsEngine extends PhysicsBase { if (this.physicsWorker) { this.physicsWorker.postMessage({ - type: 'stabilization', + type: 'stabilize', data: { targetIterations: iterations } diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index d21812ca..67d9f4bf 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -37,9 +37,12 @@ class PhysicsWorker extends PhysicsBase { case 'removeElements': this.removeElements(msg.data); break; - case 'stabilization': + case 'stabilize': this.stabilize(msg.data); break; + case 'setStabilized': + this.stabilized = msg.data; + break; case 'initPhysicsData': console.debug('init physics data'); this.initPhysicsData(msg.data); @@ -123,6 +126,9 @@ class PhysicsWorker extends PhysicsBase { if (opts.mass !== undefined) { optionsNode.options.mass = opts.mass; } + if (opts.edges && opts.edges.length) { + optionsNode.edges.length = opts.edges.length; + } } else { console.warn('sending properties to unknown node', data.id, data.options); } From 81890032ee42f8cff8123d0f18ab136897db9680 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Mon, 26 Oct 2015 11:17:42 -0400 Subject: [PATCH 16/17] update change physics options on nodes --- lib/network/modules/PhysicsEngine.js | 13 ++++++++++-- lib/network/modules/PhysicsWorker.js | 31 +++------------------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index e217727b..bd81ea94 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -158,7 +158,15 @@ class PhysicsEngine extends PhysicsBase { */ initEmbeddedPhysics() { this.positionUpdateHandler = () => {}; - this.physicsUpdateHandler = () => {}; + this.physicsUpdateHandler = (properties) => { + if (properties.options.physics !== undefined) { + // we've received a node that has changed physics state + // so rebuild physicsBody + this.initPhysicsData(); + } + // else we're accessing the information directly out of the node + // so no need to do anything. + }; if (this.physicsWorker) { this.options.useWorker = false; this.physicsWorker.terminate(); @@ -172,11 +180,13 @@ class PhysicsEngine extends PhysicsBase { if (!this.physicsWorker) { // setup path to webworker javascript file if (!__webpack_public_path__) { + // search for element with id of 'visjs' let parentScript = document.getElementById('visjs'); if (parentScript) { let src = parentScript.getAttribute('src') __webpack_public_path__ = src.substr(0, src.lastIndexOf('/') + 1); } else { + // search all scripts for 'vis.js' let scripts = document.getElementsByTagName('script'); for (let i = 0; i < scripts.length; i++) { let src = scripts[i].getAttribute('src'); @@ -383,7 +393,6 @@ class PhysicsEngine extends PhysicsBase { } } - // TODO determine when startedStabilization needs to be propogated from the worker /** * trigger the stabilized event. * @private diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index 67d9f4bf..dfc13a92 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -22,6 +22,7 @@ class PhysicsWorker extends PhysicsBase { var msg = event.data; switch (msg.type) { case 'physicsTick': + this.processRemovals(); this.physicsTick(); this.sendPositions(); break; @@ -57,24 +58,6 @@ class PhysicsWorker extends PhysicsBase { } } -// physicsTick() { -// if(this.physicsTimeout) { -// // cancel any outstanding requests to prevent manipulation of data while iterating -// // and we're going to handle it next anyways. -// clearTimeout(this.physicsTimeout); -// } -// this.processRemovals(); -// if (this.options.enabled) { -// this.calculateForces(); -// this.moveNodes(); -// // Handle the case where physics was enabled, data was removed -// // but this physics tick was longer than the timeout and during that delta -// // physics was disabled. -// this.processRemovals(); -// } -// this.stabilizationIterations++; -// } - sendPositions() { let nodeIndices = this.physicsBody.physicsNodeIndices; let positions = {}; @@ -182,17 +165,9 @@ class PhysicsWorker extends PhysicsBase { this.toRemove.nodeIds.push.apply(this.toRemove.nodeIds, data.nodeIds); this.toRemove.edgeIds.push.apply(this.toRemove.edgeIds, data.edgeIds); // Handle case where physics is disabled. - if(this.physicsTimeout) { - // don't schedule more than one physicsTick - clearTimeout(this.physicsTimeout); + if (!this.options.enabled) { + this.processRemovals(); } - this.physicsTimeout = setTimeout(()=> { - // if physics is still enabled, the next tick will handle removeElements - if (!this.options.enabled) { - this.physicsTimeout = null; - this.physicsTick(); - } - }, 250); } processRemovals() { From d245c236673c52539275eec2adfb8e4794db8c34 Mon Sep 17 00:00:00 2001 From: Eric VanDever Date: Tue, 27 Oct 2015 15:28:21 -0400 Subject: [PATCH 17/17] fix stabilize (non-batch) events from web worker --- lib/network/modules/PhysicsEngine.js | 13 +++++++++++-- lib/network/modules/PhysicsWorker.js | 21 +++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index bd81ea94..319403b4 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -260,12 +260,12 @@ class PhysicsEngine extends PhysicsBase { physicsWorkerMessageHandler(event) { var msg = event.data; switch (msg.type) { - case 'positions': + case 'tickResults': this.stabilized = msg.data.stabilized; + this.stabilizationIterations = msg.data.stabilizationIterations; this._receivedPositions(msg.data.positions); break; case 'finalizeStabilization': - this.stabilizationIterations = msg.data.stabilizationIterations; this._finalizeStabilization(); break; case 'emit': @@ -393,6 +393,14 @@ class PhysicsEngine extends PhysicsBase { } } + _sendWorkerStabilized() { + if (this.physicsWorker) { + this.physicsWorker.postMessage({ + type: 'stabilized' + }); + } + } + /** * trigger the stabilized event. * @private @@ -403,6 +411,7 @@ class PhysicsEngine extends PhysicsBase { this.body.emitter.emit('stabilized', {iterations: amountOfIterations}); this.startedStabilization = false; this.stabilizationIterations = 0; + this._sendWorkerStabilized(); }, 0); } } diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index dfc13a92..abaf26ee 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -13,7 +13,6 @@ class PhysicsWorker extends PhysicsBase { nodeIds: [], edgeIds: [] }; - this.physicsTimeout = null; this.isWorker = true; this.emit = (event, data) => {this.postMessage({type: 'emit', data: {event: event, data: data}})}; } @@ -24,7 +23,7 @@ class PhysicsWorker extends PhysicsBase { case 'physicsTick': this.processRemovals(); this.physicsTick(); - this.sendPositions(); + this.sendTickResults(); break; case 'updatePositions': this.receivePositions(msg.data); @@ -44,6 +43,10 @@ class PhysicsWorker extends PhysicsBase { case 'setStabilized': this.stabilized = msg.data; break; + case 'stabilized': + this.startedStabilization = false; + this.stabilizationIterations = 0; + break; case 'initPhysicsData': console.debug('init physics data'); this.initPhysicsData(msg.data); @@ -58,7 +61,7 @@ class PhysicsWorker extends PhysicsBase { } } - sendPositions() { + sendTickResults() { let nodeIndices = this.physicsBody.physicsNodeIndices; let positions = {}; for (let i = 0; i < nodeIndices.length; i++) { @@ -68,10 +71,11 @@ class PhysicsWorker extends PhysicsBase { } this.postMessage({ - type: 'positions', + type: 'tickResults', data: { positions: positions, - stabilized: this.stabilized + stabilized: this.stabilized, + stabilizationIterations: this.stabilizationIterations } }); } @@ -254,12 +258,9 @@ class PhysicsWorker extends PhysicsBase { } _finalizeStabilization() { - this.sendPositions(); + this.sendTickResults(); this.postMessage({ - type: 'finalizeStabilization', - data: { - stabilizationIterations: this.stabilizationIterations - } + type: 'finalizeStabilization' }); } }