diff --git a/dist/vis.js b/dist/vis.js index 28fc9181..cc040f2a 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -3156,7 +3156,7 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; - var Emitter = __webpack_require__(69); + var Emitter = __webpack_require__(43); var DataSet = __webpack_require__(3); var DataView = __webpack_require__(4); var util = __webpack_require__(1); @@ -6319,23 +6319,23 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; - var Emitter = __webpack_require__(69); + var Emitter = __webpack_require__(43); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); var DataView = __webpack_require__(4); var Range = __webpack_require__(17); - var Core = __webpack_require__(43); + var Core = __webpack_require__(44); var TimeAxis = __webpack_require__(35); var CurrentTime = __webpack_require__(26); var CustomTime = __webpack_require__(27); var ItemSet = __webpack_require__(32); - var Configurator = __webpack_require__(44); - var Validator = __webpack_require__(45)['default']; - var printStyle = __webpack_require__(45).printStyle; - var allOptions = __webpack_require__(46).allOptions; - var configureOptions = __webpack_require__(46).configureOptions; + var Configurator = __webpack_require__(45); + var Validator = __webpack_require__(46)['default']; + var printStyle = __webpack_require__(46).printStyle; + var allOptions = __webpack_require__(47).allOptions; + var configureOptions = __webpack_require__(47).configureOptions; /** * Create a timeline visualization @@ -6760,23 +6760,23 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; - var Emitter = __webpack_require__(69); + var Emitter = __webpack_require__(43); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); var DataView = __webpack_require__(4); var Range = __webpack_require__(17); - var Core = __webpack_require__(43); + var Core = __webpack_require__(44); var TimeAxis = __webpack_require__(35); var CurrentTime = __webpack_require__(26); var CustomTime = __webpack_require__(27); var LineGraph = __webpack_require__(34); - var Configurator = __webpack_require__(44); - var Validator = __webpack_require__(45)['default']; - var printStyle = __webpack_require__(45).printStyle; - var allOptions = __webpack_require__(47).allOptions; - var configureOptions = __webpack_require__(47).configureOptions; + var Configurator = __webpack_require__(45); + var Validator = __webpack_require__(46)['default']; + var printStyle = __webpack_require__(46).printStyle; + var allOptions = __webpack_require__(48).allOptions; + var configureOptions = __webpack_require__(48).configureOptions; /** * Create a timeline visualization @@ -7778,7 +7778,7 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(48); + var hammerUtil = __webpack_require__(49); var moment = __webpack_require__(40); var Component = __webpack_require__(25); var DateUtil = __webpack_require__(15); @@ -10526,7 +10526,7 @@ return /******/ (function(modules) { // webpackBootstrap var util = __webpack_require__(1); var Component = __webpack_require__(25); var moment = __webpack_require__(40); - var locales = __webpack_require__(49); + var locales = __webpack_require__(50); /** * A current time bar @@ -10703,7 +10703,7 @@ return /******/ (function(modules) { // webpackBootstrap var util = __webpack_require__(1); var Component = __webpack_require__(25); var moment = __webpack_require__(40); - var locales = __webpack_require__(49); + var locales = __webpack_require__(50); /** * A custom time bar @@ -11544,9 +11544,9 @@ return /******/ (function(modules) { // webpackBootstrap var util = __webpack_require__(1); var DOMutil = __webpack_require__(2); - var Line = __webpack_require__(50); - var Bar = __webpack_require__(51); - var Points = __webpack_require__(52); + var Line = __webpack_require__(51); + var Bar = __webpack_require__(52); + var Points = __webpack_require__(53); /** * /** @@ -14217,8 +14217,8 @@ return /******/ (function(modules) { // webpackBootstrap var DataAxis = __webpack_require__(28); var GraphGroup = __webpack_require__(29); var Legend = __webpack_require__(33); - var BarFunctions = __webpack_require__(51); - var LineFunctions = __webpack_require__(50); + var BarFunctions = __webpack_require__(52); + var LineFunctions = __webpack_require__(51); var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items @@ -15627,68 +15627,68 @@ return /******/ (function(modules) { // webpackBootstrap function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - var _modulesGroups = __webpack_require__(53); + var _modulesGroups = __webpack_require__(54); var _modulesGroups2 = _interopRequireDefault(_modulesGroups); - var _modulesNodesHandler = __webpack_require__(54); + var _modulesNodesHandler = __webpack_require__(55); var _modulesNodesHandler2 = _interopRequireDefault(_modulesNodesHandler); - var _modulesEdgesHandler = __webpack_require__(55); + var _modulesEdgesHandler = __webpack_require__(56); var _modulesEdgesHandler2 = _interopRequireDefault(_modulesEdgesHandler); - var _modulesPhysicsEngine = __webpack_require__(56); + var _modulesPhysicsEngine = __webpack_require__(57); var _modulesPhysicsEngine2 = _interopRequireDefault(_modulesPhysicsEngine); - var _modulesClustering = __webpack_require__(57); + var _modulesClustering = __webpack_require__(58); var _modulesClustering2 = _interopRequireDefault(_modulesClustering); - var _modulesCanvasRenderer = __webpack_require__(58); + var _modulesCanvasRenderer = __webpack_require__(59); var _modulesCanvasRenderer2 = _interopRequireDefault(_modulesCanvasRenderer); - var _modulesCanvas = __webpack_require__(59); + var _modulesCanvas = __webpack_require__(60); var _modulesCanvas2 = _interopRequireDefault(_modulesCanvas); - var _modulesView = __webpack_require__(60); + var _modulesView = __webpack_require__(61); var _modulesView2 = _interopRequireDefault(_modulesView); - var _modulesInteractionHandler = __webpack_require__(61); + var _modulesInteractionHandler = __webpack_require__(62); var _modulesInteractionHandler2 = _interopRequireDefault(_modulesInteractionHandler); - var _modulesSelectionHandler = __webpack_require__(62); + var _modulesSelectionHandler = __webpack_require__(63); var _modulesSelectionHandler2 = _interopRequireDefault(_modulesSelectionHandler); - var _modulesLayoutEngine = __webpack_require__(63); + var _modulesLayoutEngine = __webpack_require__(64); var _modulesLayoutEngine2 = _interopRequireDefault(_modulesLayoutEngine); - var _modulesManipulationSystem = __webpack_require__(64); + var _modulesManipulationSystem = __webpack_require__(65); var _modulesManipulationSystem2 = _interopRequireDefault(_modulesManipulationSystem); - var _sharedConfigurator = __webpack_require__(44); + var _sharedConfigurator = __webpack_require__(45); var _sharedConfigurator2 = _interopRequireDefault(_sharedConfigurator); - var _sharedValidator = __webpack_require__(45); + var _sharedValidator = __webpack_require__(46); var _sharedValidator2 = _interopRequireDefault(_sharedValidator); - var _optionsJs = __webpack_require__(65); + var _optionsJs = __webpack_require__(66); // Load custom shapes into CanvasRenderingContext2D - __webpack_require__(66); + __webpack_require__(67); - var Emitter = __webpack_require__(69); + var Emitter = __webpack_require__(43); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); @@ -15696,8 +15696,8 @@ return /******/ (function(modules) { // webpackBootstrap var dotparser = __webpack_require__(38); var gephiParser = __webpack_require__(39); var Images = __webpack_require__(37); - var Activator = __webpack_require__(67); - var locales = __webpack_require__(68); + var Activator = __webpack_require__(68); + var locales = __webpack_require__(69); /** * @constructor Network @@ -16193,8 +16193,8 @@ return /******/ (function(modules) { // webpackBootstrap Network.prototype.getScale = function () { return this.view.getScale.apply(this.view, arguments); }; - Network.prototype.getPosition = function () { - return this.view.getPosition.apply(this.view, arguments); + Network.prototype.getViewPosition = function () { + return this.view.getViewPosition.apply(this.view, arguments); }; Network.prototype.fit = function () { return this.view.fit.apply(this.view, arguments); @@ -16294,6 +16294,8 @@ return /******/ (function(modules) { // webpackBootstrap * * DOT language reference: http://www.graphviz.org/doc/info/lang.html * + * DOT language attributes: http://graphviz.org/content/attrs + * * @param {String} data Text containing a graph in DOT-notation * @return {Object} graph An object containing two parameters: * {Object[]} nodes @@ -16306,6 +16308,20 @@ return /******/ (function(modules) { // webpackBootstrap return parseGraph(); } + // mapping of attributes from DOT (the keys) to vis.js (the values) + var NODE_ATTR_MAPPING = { + 'fontsize': 'font.size', + 'fontcolor': 'font.color', + 'labelfontcolor': 'font.color', + 'fontname': 'font.face', + 'color': ['color.border', 'color.background'], + 'fillcolor': 'color.background', + 'tooltip': 'title', + 'labeltooltip': 'title' + }; + var EDGE_ATTR_MAPPING = Object.create(NODE_ATTR_MAPPING); + EDGE_ATTR_MAPPING.color = 'color.color'; + // token types enumeration var TOKENTYPE = { NULL: 0, @@ -17007,6 +17023,62 @@ return /******/ (function(modules) { // webpackBootstrap } } + /** + * Set a nested property on an object + * When nested objects are missing, they will be created. + * For example setProp({}, 'font.color', 'red') will return {font: {color: 'red'}} + * @param {Object} object + * @param {string} path A dot separated string like 'font.color' + * @param {*} value Value for the property + * @return {Object} Returns the original object, allows for chaining. + */ + function setProp(object, path, value) { + var names = path.split('.'); + var prop = names.pop(); + + // traverse over the nested objects + var obj = object; + for (var i = 0; i < names.length; i++) { + var name = names[i]; + if (!(name in obj)) { + obj[name] = {}; + } + obj = obj[name]; + } + + // set the property value + obj[prop] = value; + + return object; + } + + /** + * Convert an object with DOT attributes to their vis.js equivalents. + * @param {Object} attr Object with DOT attributes + * @param {Object} mapping + * @return {Object} Returns an object with vis.js attributes + */ + function convertAttr(attr, mapping) { + var converted = {}; + + for (var prop in attr) { + if (attr.hasOwnProperty(prop)) { + var visProp = mapping[prop]; + if (Array.isArray(visProp)) { + visProp.forEach(function (visPropI) { + setProp(converted, visPropI, attr[prop]); + }); + } else if (typeof visProp === 'string') { + setProp(converted, visProp, attr[prop]); + } else { + setProp(converted, prop, attr[prop]); + } + } + } + + return converted; + } + /** * Convert a string containing a graph in DOT language into a map containing * with nodes and edges in the format of graph. @@ -17029,7 +17101,7 @@ return /******/ (function(modules) { // webpackBootstrap id: dotNode.id, label: String(dotNode.label || dotNode.id) }; - merge(graphNode, dotNode.attr); + merge(graphNode, convertAttr(dotNode.attr, NODE_ATTR_MAPPING)); if (graphNode.image) { graphNode.shape = 'image'; } @@ -17049,7 +17121,7 @@ return /******/ (function(modules) { // webpackBootstrap from: dotEdge.from, to: dotEdge.to }; - merge(graphEdge, dotEdge.attr); + merge(graphEdge, convertAttr(dotEdge.attr, EDGE_ATTR_MAPPING)); graphEdge.arrows = dotEdge.type === '->' ? 'to' : undefined; return graphEdge; @@ -17065,6 +17137,9 @@ return /******/ (function(modules) { // webpackBootstrap }; } + // TODO: support of solid/dotted/dashed edges (attr = 'style') + // TODO: support for attributes 'dir' and 'arrowhead' (edge arrows) + if (dotEdge.to instanceof Object) { to = dotEdge.to.nodes; } else { @@ -17429,18 +17504,188 @@ return /******/ (function(modules) { // webpackBootstrap /* 43 */ /***/ function(module, exports, __webpack_require__) { + + /** + * Expose `Emitter`. + */ + + module.exports = Emitter; + + /** + * Initialize a new `Emitter`. + * + * @api public + */ + + function Emitter(obj) { + if (obj) return mixin(obj); + }; + + /** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; + } + + /** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.on = + Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []) + .push(fn); + return this; + }; + + /** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; + + function on() { + self.off(event, on); + fn.apply(this, arguments); + } + + on.fn = fn; + this.on(event, on); + return this; + }; + + /** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.off = + Emitter.prototype.removeListener = + Emitter.prototype.removeAllListeners = + Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks[event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks[event]; + return this; + } + + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; + }; + + /** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ + + Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks[event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; + }; + + /** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ + + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || []; + }; + + /** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ + + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; + }; + + +/***/ }, +/* 44 */ +/***/ function(module, exports, __webpack_require__) { + 'use strict'; - var Emitter = __webpack_require__(69); + var Emitter = __webpack_require__(43); var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(48); + var hammerUtil = __webpack_require__(49); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); var DataView = __webpack_require__(4); var Range = __webpack_require__(17); var ItemSet = __webpack_require__(32); var TimeAxis = __webpack_require__(35); - var Activator = __webpack_require__(67); + var Activator = __webpack_require__(68); var DateUtil = __webpack_require__(15); var CustomTime = __webpack_require__(27); @@ -18392,7 +18637,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Core; /***/ }, -/* 44 */ +/* 45 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -18407,7 +18652,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _ColorPicker = __webpack_require__(73); + var _ColorPicker = __webpack_require__(113); var _ColorPicker2 = _interopRequireDefault(_ColorPicker); @@ -18443,7 +18688,8 @@ return /******/ (function(modules) { // webpackBootstrap this.defaultOptions = { enabled: false, filter: true, - container: undefined + container: undefined, + showButton: true }; util.extend(this.options, this.defaultOptions); @@ -18477,6 +18723,9 @@ return /******/ (function(modules) { // webpackBootstrap if (options.filter !== undefined) { this.options.filter = options.filter; } + if (options.showButton !== undefined) { + this.options.showButton = options.showButton; + } if (options.enabled !== undefined) { enabled = options.enabled; } @@ -18525,7 +18774,7 @@ return /******/ (function(modules) { // webpackBootstrap show = false; if (typeof filter === 'function') { show = filter(option, []); - show = show || this._handleObject(this.configureOptions[option], [option]); + show = show || this._handleObject(this.configureOptions[option], [option], true); } else if (filter === true || filter.indexOf(option) !== -1) { show = true; } @@ -18546,24 +18795,29 @@ return /******/ (function(modules) { // webpackBootstrap counter++; } } - var generateButton = document.createElement('div'); - generateButton.className = 'vis-network-configuration button'; - generateButton.innerHTML = 'generate options'; - generateButton.onclick = function () { - _this._printOptions(); - }; - generateButton.onmouseover = function () { - generateButton.className = 'vis-network-configuration button hover'; - }; - generateButton.onmouseout = function () { - generateButton.className = 'vis-network-configuration button'; - }; - - this.optionsContainer = document.createElement('div'); - this.optionsContainer.className = 'vis-network-configuration vis-option-container'; - this.domElements.push(this.optionsContainer); - this.domElements.push(generateButton); + if (this.options.showButton === true) { + (function () { + var generateButton = document.createElement('div'); + generateButton.className = 'vis-network-configuration button'; + generateButton.innerHTML = 'generate options'; + generateButton.onclick = function () { + _this._printOptions(); + }; + generateButton.onmouseover = function () { + generateButton.className = 'vis-network-configuration button hover'; + }; + generateButton.onmouseout = function () { + generateButton.className = 'vis-network-configuration button'; + }; + + _this.optionsContainer = document.createElement('div'); + _this.optionsContainer.className = 'vis-network-configuration vis-option-container'; + + _this.domElements.push(_this.optionsContainer); + _this.domElements.push(generateButton); + })(); + } this._push(); this.colorPicker.insertTo(this.container); @@ -18905,23 +19159,31 @@ return /******/ (function(modules) { // webpackBootstrap */ value: function _handleObject(obj) { var path = arguments[1] === undefined ? [] : arguments[1]; + var checkOnly = arguments[2] === undefined ? false : arguments[2]; var show = false; var filter = this.options.filter; var visibleInSet = false; for (var subObj in obj) { if (obj.hasOwnProperty(subObj)) { - show = false; + show = true; + var item = obj[subObj]; + var newPath = util.copyAndExtendArray(path, subObj); if (typeof filter === 'function') { show = filter(subObj, path); - } else if (filter === true) { - show = true; + + // if needed we must go deeper into the object. + if (show === false) { + if (!(item instanceof Array) && typeof item !== 'string' && typeof item !== 'boolean' && item instanceof Object) { + this.allowCreation = false; + show = this._handleObject(item, newPath, true); + this.allowCreation = checkOnly === false; + } + } } if (show !== false) { visibleInSet = true; - var item = obj[subObj]; - var newPath = util.copyAndExtendArray(path, subObj); var value = this._getValue(newPath); if (item instanceof Array) { @@ -19050,7 +19312,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 45 */ +/* 46 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -19369,7 +19631,7 @@ return /******/ (function(modules) { // webpackBootstrap // item is a function, which is allowed /***/ }, -/* 46 */ +/* 47 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -19586,7 +19848,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.configureOptions = configureOptions; /***/ }, -/* 47 */ +/* 48 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -19857,7 +20119,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.configureOptions = configureOptions; /***/ }, -/* 48 */ +/* 49 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -19929,7 +20191,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.offRelease = exports.offTouch; /***/ }, -/* 49 */ +/* 50 */ /***/ function(module, exports, __webpack_require__) { // English @@ -19951,13 +20213,13 @@ return /******/ (function(modules) { // webpackBootstrap exports['nl_BE'] = exports['nl']; /***/ }, -/* 50 */ +/* 51 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var DOMutil = __webpack_require__(2); - var Points = __webpack_require__(52); + var Points = __webpack_require__(53); function Line(groupId, options) { this.groupId = groupId; @@ -20246,13 +20508,13 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Line; /***/ }, -/* 51 */ +/* 52 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var DOMutil = __webpack_require__(2); - var Points = __webpack_require__(52); + var Points = __webpack_require__(53); function Bargraph(groupId, options) { this.groupId = groupId; @@ -20494,7 +20756,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Bargraph; /***/ }, -/* 52 */ +/* 53 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -20541,7 +20803,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Points; /***/ }, -/* 53 */ +/* 54 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -20683,7 +20945,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 54 */ +/* 55 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -20698,11 +20960,11 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsNode = __webpack_require__(74); + var _componentsNode = __webpack_require__(73); var _componentsNode2 = _interopRequireDefault(_componentsNode); - var _componentsSharedLabel = __webpack_require__(75); + var _componentsSharedLabel = __webpack_require__(74); var _componentsSharedLabel2 = _interopRequireDefault(_componentsSharedLabel); @@ -20739,7 +21001,7 @@ return /******/ (function(modules) { // webpackBootstrap this.options = {}; this.defaultOptions = { borderWidth: 1, - borderWidthSelected: undefined, + borderWidthSelected: 2, brokenImage: undefined, color: { border: '#2B7CE9', @@ -21114,12 +21376,14 @@ return /******/ (function(modules) { // webpackBootstrap var nodeObj = {}; // used to quickly check if node already exists for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; - if (edge.toId === nodeId) { + if (edge.toId == nodeId) { + // these are double equals since ids can be numeric or string if (nodeObj[edge.fromId] === undefined) { nodeList.push(edge.fromId); nodeObj[edge.fromId] = true; } - } else if (edge.fromId === nodeId) { + } else if (edge.fromId == nodeId) { + // these are double equals since ids can be numeric or string if (nodeObj[edge.toId] === undefined) { nodeList.push(edge.toId); nodeObj[edge.toId] = true; @@ -21156,7 +21420,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 55 */ +/* 56 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -21171,11 +21435,11 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsEdge = __webpack_require__(76); + var _componentsEdge = __webpack_require__(75); var _componentsEdge2 = _interopRequireDefault(_componentsEdge); - var _componentsSharedLabel = __webpack_require__(75); + var _componentsSharedLabel = __webpack_require__(74); var _componentsSharedLabel2 = _interopRequireDefault(_componentsSharedLabel); @@ -21256,7 +21520,7 @@ return /******/ (function(modules) { // webpackBootstrap } } }, - selectionWidth: 1, + selectionWidth: 1.5, selfReferenceSize: 20, shadow: { enabled: false, @@ -21575,7 +21839,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 56 */ +/* 57 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -21590,35 +21854,35 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsPhysicsBarnesHutSolver = __webpack_require__(77); + var _componentsPhysicsBarnesHutSolver = __webpack_require__(76); var _componentsPhysicsBarnesHutSolver2 = _interopRequireDefault(_componentsPhysicsBarnesHutSolver); - var _componentsPhysicsRepulsionSolver = __webpack_require__(78); + var _componentsPhysicsRepulsionSolver = __webpack_require__(77); var _componentsPhysicsRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsRepulsionSolver); - var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(79); + var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(78); var _componentsPhysicsHierarchicalRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalRepulsionSolver); - var _componentsPhysicsSpringSolver = __webpack_require__(80); + var _componentsPhysicsSpringSolver = __webpack_require__(79); var _componentsPhysicsSpringSolver2 = _interopRequireDefault(_componentsPhysicsSpringSolver); - var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(81); + var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(80); var _componentsPhysicsHierarchicalSpringSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalSpringSolver); - var _componentsPhysicsCentralGravitySolver = __webpack_require__(82); + var _componentsPhysicsCentralGravitySolver = __webpack_require__(81); var _componentsPhysicsCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsCentralGravitySolver); - var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(83); + var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(82); var _componentsPhysicsFA2BasedRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedRepulsionSolver); - var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(84); + var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(83); var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver); @@ -21685,7 +21949,7 @@ return /******/ (function(modules) { // webpackBootstrap stabilization: { enabled: true, iterations: 1000, // maximum number of iteration to stabilize - updateInterval: 100, + updateInterval: 50, onlyDynamicEdges: false, fit: true }, @@ -22198,22 +22462,22 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 57 */ +/* 58 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; + 'use strict'; - Object.defineProperty(exports, "__esModule", { + 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 _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 _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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsNodesCluster = __webpack_require__(85); + var _componentsNodesCluster = __webpack_require__(84); var _componentsNodesCluster2 = _interopRequireDefault(_componentsNodesCluster); @@ -22221,6 +22485,8 @@ return /******/ (function(modules) { // webpackBootstrap var ClusterEngine = (function () { function ClusterEngine(body) { + var _this = this; + _classCallCheck(this, ClusterEngine); this.body = body; @@ -22229,15 +22495,19 @@ return /******/ (function(modules) { // webpackBootstrap this.options = {}; this.defaultOptions = {}; util.extend(this.options, this.defaultOptions); + + this.body.emitter.on('_resetData', function () { + _this.clusteredNodes = {}; + }); } _createClass(ClusterEngine, [{ - key: "setOptions", + key: 'setOptions', value: function setOptions(options) { if (options !== undefined) {} } }, { - key: "clusterByHubsize", + key: 'clusterByHubsize', /** * @@ -22247,7 +22517,7 @@ return /******/ (function(modules) { // webpackBootstrap value: function clusterByHubsize(hubsize, options) { if (hubsize === undefined) { hubsize = this._getHubSize(); - } else if (tyepof(hubsize) === "object") { + } else if (tyepof(hubsize) === 'object') { options = this._checkOptions(hubsize); hubsize = this._getHubSize(); } @@ -22263,10 +22533,10 @@ return /******/ (function(modules) { // webpackBootstrap for (var i = 0; i < nodesToCluster.length; i++) { this.clusterByConnection(nodesToCluster[i], options, false); } - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } }, { - key: "cluster", + key: 'cluster', /** * loop over all nodes, check if they adhere to the condition and cluster if needed. @@ -22278,7 +22548,7 @@ return /******/ (function(modules) { // webpackBootstrap var refreshData = 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 @@ -22306,7 +22576,7 @@ return /******/ (function(modules) { // webpackBootstrap this._cluster(childNodesObj, childEdgesObj, options, refreshData); } }, { - key: "clusterOutliers", + key: 'clusterOutliers', /** * Cluster all nodes in the network that have only 1 edge @@ -22324,28 +22594,41 @@ return /******/ (function(modules) { // webpackBootstrap var childNodesObj = {}; var childEdgesObj = {}; var nodeId = this.body.nodeIndices[i]; - if (this.body.nodes[nodeId].edges.length === 1) { + var visibleEdges = 0; + var edge = undefined; + for (var j = 0; j < this.body.nodes[nodeId].edges.length; j++) { + if (this.body.nodes[nodeId].edges[j].options.hidden === false) { + visibleEdges++; + edge = this.body.nodes[nodeId].edges[j]; + } + } + + if (visibleEdges === 1) { // this is an outlier - var edge = this.body.nodes[nodeId].edges[0]; var childNodeId = this._getConnectedId(edge, nodeId); if (childNodeId !== nodeId) { if (options.joinCondition === undefined) { - childEdgesObj[edge.id] = edge; - childNodesObj[nodeId] = this.body.nodes[nodeId]; - childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + if (this._checkIfUsed(clusters, nodeId, edge.id) === false && this._checkIfUsed(clusters, childNodeId, edge.id) === false) { + childEdgesObj[edge.id] = edge; + childNodesObj[nodeId] = this.body.nodes[nodeId]; + childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + } } else { var clonedOptions = this._cloneOptions(this.body.nodes[nodeId]); - if (options.joinCondition(clonedOptions) === true) { + if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters, nodeId, edge.id) === false) { childEdgesObj[edge.id] = edge; childNodesObj[nodeId] = this.body.nodes[nodeId]; } clonedOptions = this._cloneOptions(this.body.nodes[childNodeId]); - if (options.joinCondition(clonedOptions) === true) { + if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters, nodeId, edge.id) === false) { childEdgesObj[edge.id] = edge; childNodesObj[childNodeId] = this.body.nodes[childNodeId]; } } - clusters.push({ nodes: childNodesObj, edges: childEdgesObj }); + + if (Object.keys(childNodesObj).length > 0 && Object.keys(childEdgesObj).length > 0) { + clusters.push({ nodes: childNodesObj, edges: childEdgesObj }); + } } } } @@ -22355,11 +22638,22 @@ return /******/ (function(modules) { // webpackBootstrap } if (refreshData === true) { - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); + } + } + }, { + key: '_checkIfUsed', + value: function _checkIfUsed(clusters, nodeId, edgeId) { + for (var i = 0; i < clusters.length; i++) { + var cluster = clusters[i]; + if (cluster.nodes[nodeId] !== undefined || cluster.edges[edgeId] !== undefined) { + return true; + } } + return false; } }, { - key: "clusterByConnection", + key: 'clusterByConnection', /** * suck all connected nodes of a node into the node. @@ -22372,10 +22666,10 @@ return /******/ (function(modules) { // webpackBootstrap // 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]; @@ -22423,7 +22717,7 @@ return /******/ (function(modules) { // webpackBootstrap this._cluster(childNodesObj, childEdgesObj, options, refreshData); } }, { - key: "_cloneOptions", + 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. @@ -22434,7 +22728,7 @@ return /******/ (function(modules) { // webpackBootstrap */ value: function _cloneOptions(item, type) { var clonedOptions = {}; - if (type === undefined || type === "node") { + if (type === undefined || type === 'node') { util.deepExtend(clonedOptions, item.options, true); clonedOptions.x = item.x; clonedOptions.y = item.y; @@ -22445,7 +22739,7 @@ return /******/ (function(modules) { // webpackBootstrap return clonedOptions; } }, { - key: "_createClusterEdges", + key: '_createClusterEdges', /** * This function creates the edges that will be attached to the cluster. @@ -22488,18 +22782,18 @@ return /******/ (function(modules) { // webpackBootstrap // if the node connected to the cluster is also in the cluster we do not need a new edge. if (childNodesObj[otherNodeId] === undefined) { - var clonedOptions = this._cloneOptions(edge, "edge"); + var clonedOptions = this._cloneOptions(edge, 'edge'); util.deepExtend(clonedOptions, clusterEdgeProperties); clonedOptions.from = fromId; clonedOptions.to = toId; - clonedOptions.id = "clusterEdge:" + util.randomUUID(); + clonedOptions.id = 'clusterEdge:' + util.randomUUID(); newEdges.push(this.body.functions.createEdge(clonedOptions)); } } } } }, { - key: "_checkOptions", + key: '_checkOptions', /** * This function checks the options that can be supplied to the different cluster functions @@ -22521,7 +22815,7 @@ return /******/ (function(modules) { // webpackBootstrap return options; } }, { - key: "_cluster", + key: '_cluster', /** * @@ -22541,12 +22835,6 @@ return /******/ (function(modules) { // webpackBootstrap var clusterNodeProperties = util.deepExtend({}, options.clusterNodeProperties); - // check if we have an unique id; - if (clusterNodeProperties.id === undefined) { - clusterNodeProperties.id = "cluster:" + util.randomUUID(); - } - var clusterId = clusterNodeProperties.id; - // construct the clusterNodeProperties if (options.processProperties !== undefined) { // get the childNode options @@ -22559,18 +22847,24 @@ return /******/ (function(modules) { // webpackBootstrap // get clusterproperties based on childNodes var childEdgesOptions = []; for (var edgeId in childEdgesObj) { - var clonedOptions = this._cloneOptions(childEdgesObj[edgeId], "edge"); + var clonedOptions = this._cloneOptions(childEdgesObj[edgeId], 'edge'); childEdgesOptions.push(clonedOptions); } 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!'); } } + // check if we have an unique id; + if (clusterNodeProperties.id === undefined) { + clusterNodeProperties.id = 'cluster:' + util.randomUUID(); + } + var clusterId = clusterNodeProperties.id; + if (clusterNodeProperties.label === undefined) { - clusterNodeProperties.label = "cluster"; + clusterNodeProperties.label = 'cluster'; } // give the clusterNode a postion if it does not have one. @@ -22590,7 +22884,7 @@ return /******/ (function(modules) { // webpackBootstrap clusterNodeProperties.id = clusterId; // create the clusterNode - var clusterNode = this.body.functions.createNode(clusterNodeProperties, _componentsNodesCluster2["default"]); + var clusterNode = this.body.functions.createNode(clusterNodeProperties, _componentsNodesCluster2['default']); clusterNode.isCluster = true; clusterNode.containedNodes = childNodesObj; clusterNode.containedEdges = childEdgesObj; @@ -22635,11 +22929,11 @@ return /******/ (function(modules) { // webpackBootstrap // wrap up if (refreshData === true) { - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } } }, { - key: "isCluster", + key: 'isCluster', /** * Check if a node is a cluster. @@ -22650,12 +22944,12 @@ return /******/ (function(modules) { // webpackBootstrap 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", + key: '_getClusterPosition', /** * get the position of the cluster node based on what's inside @@ -22681,7 +22975,7 @@ return /******/ (function(modules) { // webpackBootstrap return { x: 0.5 * (minX + maxX), y: 0.5 * (minY + maxY) }; } }, { - key: "openCluster", + key: 'openCluster', /** * Open a cluster by calling this function. @@ -22693,13 +22987,13 @@ return /******/ (function(modules) { // webpackBootstrap // 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]; @@ -22737,7 +23031,8 @@ return /******/ (function(modules) { // webpackBootstrap edge.disconnect(); delete this.body.edges[edgeId]; } else { - // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster and make a new temporary edge. + + // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster so it will be released when that cluster is opened. if (this.clusteredNodes[edge.fromId] !== undefined || this.clusteredNodes[edge.toId] !== undefined) { var fromId = undefined, toId = undefined; @@ -22746,22 +23041,25 @@ return /******/ (function(modules) { // webpackBootstrap var _clusterNode = this.body.nodes[clusterId]; _clusterNode.containedEdges[edgeId] = edge; - if (this.clusteredNodes[edge.fromId] !== undefined) { - fromId = clusterId; - toId = edge.toId; - } else { - fromId = edge.fromId; - toId = clusterId; - } + // if both from and to nodes are visible, we create a new temporary edge + if (edge.from.options.hidden !== true && edge.to.options.hidden !== true) { + if (this.clusteredNodes[edge.fromId] !== undefined) { + fromId = clusterId; + toId = edge.toId; + } else { + fromId = edge.fromId; + toId = clusterId; + } - var clonedOptions = this._cloneOptions(edge, "edge"); - var id = "clusterEdge:" + util.randomUUID(); - util.deepExtend(clonedOptions, _clusterNode.clusterEdgeProperties); - util.deepExtend(clonedOptions, { from: fromId, to: toId, hidden: false, physics: true, id: id }); - var newEdge = this.body.functions.createEdge(clonedOptions); + var clonedOptions = this._cloneOptions(edge, 'edge'); + var id = 'clusterEdge:' + util.randomUUID(); + util.deepExtend(clonedOptions, _clusterNode.clusterEdgeProperties); + util.deepExtend(clonedOptions, { from: fromId, to: toId, hidden: false, physics: true, id: id }); + var newEdge = this.body.functions.createEdge(clonedOptions); - this.body.edges[id] = newEdge; - this.body.edges[id].connect(); + this.body.edges[id] = newEdge; + this.body.edges[id].connect(); + } } else { edge.options.hidden = false; edge.togglePhysics(true); @@ -22783,11 +23081,11 @@ return /******/ (function(modules) { // webpackBootstrap delete this.body.nodes[clusterNodeId]; if (refreshData === true) { - this.body.emitter.emit("_dataChanged"); + this.body.emitter.emit('_dataChanged'); } } }, { - key: "_connectEdge", + key: '_connectEdge', /** * Connect an edge that was previously contained from cluster A to cluster B if the node that it was originally connected to @@ -22813,7 +23111,7 @@ return /******/ (function(modules) { // webpackBootstrap edge.connect(); } }, { - key: "findNode", + key: 'findNode', /** * Get the stack clusterId's that a certain node resides in. cluster A -> cluster B -> cluster C -> node @@ -22835,7 +23133,7 @@ return /******/ (function(modules) { // webpackBootstrap return stack; } }, { - key: "_getConnectedId", + key: '_getConnectedId', /** * Get the Id the node is connected to @@ -22854,7 +23152,7 @@ return /******/ (function(modules) { // webpackBootstrap } } }, { - key: "_getHubSize", + key: '_getHubSize', /** * We determine how many connections denote an important hub. @@ -22897,11 +23195,11 @@ return /******/ (function(modules) { // webpackBootstrap return ClusterEngine; })(); - exports["default"] = ClusterEngine; - module.exports = exports["default"]; + exports['default'] = ClusterEngine; + module.exports = exports['default']; /***/ }, -/* 58 */ +/* 59 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -23281,7 +23579,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 59 */ +/* 60 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -23295,7 +23593,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(48); + var hammerUtil = __webpack_require__(49); var util = __webpack_require__(1); @@ -23649,7 +23947,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 60 */ +/* 61 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -24038,8 +24336,8 @@ return /******/ (function(modules) { // webpackBootstrap return this.body.view.scale; } }, { - key: "getPosition", - value: function getPosition() { + key: "getViewPosition", + value: function getViewPosition() { return { x: this.body.view.translation.x, y: this.body.view.translation.y }; } }]); @@ -24051,7 +24349,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 61 */ +/* 62 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -24066,11 +24364,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__(86); + var _componentsNavigationHandler = __webpack_require__(85); var _componentsNavigationHandler2 = _interopRequireDefault(_componentsNavigationHandler); - var _componentsPopup = __webpack_require__(87); + var _componentsPopup = __webpack_require__(86); var _componentsPopup2 = _interopRequireDefault(_componentsPopup); @@ -24505,9 +24803,9 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_requestRedraw'); if (scaleOld < scale) { - this.body.emitter.emit('zoom', { direction: '+' }); + this.body.emitter.emit('zoom', { direction: '+', scale: this.body.view.scale }); } else { - this.body.emitter.emit('zoom', { direction: '-' }); + this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale }); } } } @@ -24757,7 +25055,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 62 */ +/* 63 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -24770,8 +25068,8 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var Node = __webpack_require__(74); - var Edge = __webpack_require__(76); + var Node = __webpack_require__(73); + var Edge = __webpack_require__(75); var util = __webpack_require__(1); var SelectionHandler = (function () { @@ -25477,7 +25775,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 63 */ +/* 64 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -25974,7 +26272,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 64 */ +/* 65 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -25989,7 +26287,7 @@ return /******/ (function(modules) { // webpackBootstrap var util = __webpack_require__(1); var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(48); + var hammerUtil = __webpack_require__(49); /** * clears the toolbar div element of children @@ -27169,7 +27467,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 65 */ +/* 66 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -27199,6 +27497,7 @@ return /******/ (function(modules) { // webpackBootstrap enabled: { boolean: boolean }, filter: { boolean: boolean, string: string, array: array, fn: fn }, container: { dom: dom }, + showButton: { boolean: boolean }, __type__: { object: object, boolean: boolean, string: string, array: array, fn: fn } }, edges: { @@ -27499,11 +27798,11 @@ return /******/ (function(modules) { // webpackBootstrap min: [10, 0, 200, 1], max: [30, 0, 200, 1], label: { - enabled: true, + enabled: false, min: [14, 0, 200, 1], max: [30, 0, 200, 1], maxVisible: [30, 0, 200, 1], - drawThreshold: [3, 0, 20, 1] + drawThreshold: [5, 0, 20, 1] } }, shadow: { @@ -27534,12 +27833,12 @@ return /******/ (function(modules) { // webpackBootstrap size: [14, 0, 100, 1], // px face: ['arial', 'verdana', 'tahoma'], background: ['color', 'none'], - strokeWidth: [1, 0, 50, 1], // px + strokeWidth: [2, 0, 50, 1], // px strokeColor: ['color', '#ffffff'], align: ['horizontal', 'top', 'middle', 'bottom'] }, hidden: false, - hoverWidth: [2, 0, 5, 0.1], + hoverWidth: [1.5, 0, 5, 0.1], physics: true, scaling: { min: [1, 0, 100, 1], @@ -27549,7 +27848,7 @@ return /******/ (function(modules) { // webpackBootstrap min: [14, 0, 200, 1], max: [30, 0, 200, 1], maxVisible: [30, 0, 200, 1], - drawThreshold: [3, 0, 20, 1] + drawThreshold: [5, 0, 20, 1] } }, selectionWidth: [1.5, 0, 5, 0.1], @@ -27568,7 +27867,7 @@ return /******/ (function(modules) { // webpackBootstrap width: [1, 0, 30, 1] }, layout: { - randomSeed: [0, 0, 500, 1], + //randomSeed: [0, 0, 500, 1], hierarchical: { enabled: false, levelSeparation: [150, 20, 500, 5], @@ -27604,37 +27903,37 @@ return /******/ (function(modules) { // webpackBootstrap gravitationalConstant: [-2000, -30000, 0, 50], centralGravity: [0.3, 0, 10, 0.05], springLength: [95, 0, 500, 5], - springConstant: [0.04, 0, 5, 0.005], + springConstant: [0.04, 0, 1.2, 0.005], damping: [0.09, 0, 1, 0.01], avoidOverlap: [0, 0, 1, 0.01] }, forceAtlas2Based: { //theta: [0.5, 0.1, 1, 0.05], - gravitationalConstant: [-800, -3000, 0, 1], + gravitationalConstant: [-50, -500, 0, 1], centralGravity: [0.01, 0, 1, 0.005], springLength: [95, 0, 500, 5], - springConstant: [0.08, 0, 5, 0.005], + springConstant: [0.08, 0, 1.2, 0.005], damping: [0.4, 0, 1, 0.01], avoidOverlap: [0, 0, 1, 0.01] }, repulsion: { centralGravity: [0.2, 0, 10, 0.05], springLength: [200, 0, 500, 5], - springConstant: [0.05, 0, 5, 0.005], + springConstant: [0.05, 0, 1.2, 0.005], nodeDistance: [100, 0, 500, 5], damping: [0.09, 0, 1, 0.01] }, hierarchicalRepulsion: { centralGravity: [0.2, 0, 10, 0.05], springLength: [100, 0, 500, 5], - springConstant: [0.01, 0, 5, 0.005], + springConstant: [0.01, 0, 1.2, 0.005], nodeDistance: [120, 0, 500, 5], damping: [0.09, 0, 1, 0.01] }, maxVelocity: [50, 0, 150, 1], minVelocity: [0.1, 0.01, 0.5, 0.01], solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'], - timestep: [0.5, 0, 1, 0.05] + timestep: [0.5, 0.01, 1, 0.01] }, global: { locale: ['en', 'nl'] @@ -27645,7 +27944,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.configureOptions = configureOptions; /***/ }, -/* 66 */ +/* 67 */ /***/ function(module, exports, __webpack_require__) { /** @@ -27932,13 +28231,13 @@ return /******/ (function(modules) { // webpackBootstrap } /***/ }, -/* 67 */ +/* 68 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var keycharm = __webpack_require__(88); - var Emitter = __webpack_require__(69); + var keycharm = __webpack_require__(87); + var Emitter = __webpack_require__(43); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); @@ -28085,7 +28384,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Activator; /***/ }, -/* 68 */ +/* 69 */ /***/ function(module, exports, __webpack_require__) { // English @@ -28128,176 +28427,6 @@ return /******/ (function(modules) { // webpackBootstrap exports['nl_NL'] = exports['nl']; exports['nl_BE'] = exports['nl']; -/***/ }, -/* 69 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Expose `Emitter`. - */ - - module.exports = Emitter; - - /** - * Initialize a new `Emitter`. - * - * @api public - */ - - function Emitter(obj) { - if (obj) return mixin(obj); - }; - - /** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ - - function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; - } - - /** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - - Emitter.prototype.on = - Emitter.prototype.addEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks[event] = this._callbacks[event] || []) - .push(fn); - return this; - }; - - /** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - - Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; - - function on() { - self.off(event, on); - fn.apply(this, arguments); - } - - on.fn = fn; - this.on(event, on); - return this; - }; - - /** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - - Emitter.prototype.off = - Emitter.prototype.removeListener = - Emitter.prototype.removeAllListeners = - Emitter.prototype.removeEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; - } - - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; - - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; - } - - // remove specific handler - var cb; - for (var i = 0; i < callbacks.length; i++) { - cb = callbacks[i]; - if (cb === fn || cb.fn === fn) { - callbacks.splice(i, 1); - break; - } - } - return this; - }; - - /** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ - - Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks[event]; - - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); - } - } - - return this; - }; - - /** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public - */ - - Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks[event] || []; - }; - - /** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ - - Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; - }; - - /***/ }, /* 70 */ /***/ function(module, exports, __webpack_require__) { @@ -31385,7 +31514,7 @@ return /******/ (function(modules) { // webpackBootstrap return _moment; })); - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(107)(module))) + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(89)(module))) /***/ }, /* 71 */ @@ -34071,7 +34200,7 @@ return /******/ (function(modules) { // webpackBootstrap prefixed: prefixed }); - if ("function" == TYPE_FUNCTION && __webpack_require__(108)) { + if ("function" == TYPE_FUNCTION && __webpack_require__(90)) { !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { return Hammer; }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); @@ -34096,576 +34225,826 @@ return /******/ (function(modules) { // webpackBootstrap 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 Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(48); - var util = __webpack_require__(1); + var _sharedLabel = __webpack_require__(74); - var ColorPicker = (function () { - function ColorPicker() { - var pixelRatio = arguments[0] === undefined ? 1 : arguments[0]; + var _sharedLabel2 = _interopRequireDefault(_sharedLabel); - _classCallCheck(this, ColorPicker); + var _nodesShapesBox = __webpack_require__(91); - this.pixelRatio = pixelRatio; - this.generated = false; - this.centerCoordinates = { x: 289 / 2, y: 289 / 2 }; - this.r = 289 * 0.49; - this.color = { r: 255, g: 255, b: 255, a: 1 }; - this.hueCircle = undefined; - this.initialColor = { r: 255, g: 255, b: 255, a: 1 }; - this.previousColor = undefined; - this.applied = false; + var _nodesShapesBox2 = _interopRequireDefault(_nodesShapesBox); - // bound by - this.updateCallback = function () {}; + var _nodesShapesCircle = __webpack_require__(92); - // create all DOM elements - this._create(); - } + var _nodesShapesCircle2 = _interopRequireDefault(_nodesShapesCircle); - _createClass(ColorPicker, [{ - key: 'insertTo', + var _nodesShapesCircularImage = __webpack_require__(93); - /** - * this inserts the colorPicker into a div from the DOM - * @param container - */ - value: function insertTo(container) { - if (this.hammer !== undefined) { - this.hammer.destroy(); - this.hammer = undefined; - } - this.container = container; - this.container.appendChild(this.frame); - this._bindHammer(); + var _nodesShapesCircularImage2 = _interopRequireDefault(_nodesShapesCircularImage); - this._setSize(); - } - }, { - key: 'setCallback', + var _nodesShapesDatabase = __webpack_require__(94); - /** - * the callback is executed on apply and save. Bind it to the application - * @param callback - */ - 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.'); - } - } - }, { - key: '_isColorString', - value: function _isColorString(color) { - var htmlColors = { black: '#000000', navy: '#000080', darkblue: '#00008B', mediumblue: '#0000CD', blue: '#0000FF', darkgreen: '#006400', green: '#008000', teal: '#008080', darkcyan: '#008B8B', deepskyblue: '#00BFFF', darkturquoise: '#00CED1', mediumspringgreen: '#00FA9A', lime: '#00FF00', springgreen: '#00FF7F', aqua: '#00FFFF', cyan: '#00FFFF', midnightblue: '#191970', dodgerblue: '#1E90FF', lightseagreen: '#20B2AA', forestgreen: '#228B22', seagreen: '#2E8B57', darkslategray: '#2F4F4F', limegreen: '#32CD32', mediumseagreen: '#3CB371', turquoise: '#40E0D0', royalblue: '#4169E1', steelblue: '#4682B4', darkslateblue: '#483D8B', mediumturquoise: '#48D1CC', indigo: '#4B0082', darkolivegreen: '#556B2F', cadetblue: '#5F9EA0', cornflowerblue: '#6495ED', mediumaquamarine: '#66CDAA', dimgray: '#696969', slateblue: '#6A5ACD', olivedrab: '#6B8E23', slategray: '#708090', lightslategray: '#778899', mediumslateblue: '#7B68EE', lawngreen: '#7CFC00', chartreuse: '#7FFF00', aquamarine: '#7FFFD4', maroon: '#800000', purple: '#800080', olive: '#808000', gray: '#808080', skyblue: '#87CEEB', lightskyblue: '#87CEFA', blueviolet: '#8A2BE2', darkred: '#8B0000', darkmagenta: '#8B008B', saddlebrown: '#8B4513', darkseagreen: '#8FBC8F', lightgreen: '#90EE90', mediumpurple: '#9370D8', darkviolet: '#9400D3', palegreen: '#98FB98', darkorchid: '#9932CC', yellowgreen: '#9ACD32', sienna: '#A0522D', brown: '#A52A2A', darkgray: '#A9A9A9', lightblue: '#ADD8E6', greenyellow: '#ADFF2F', paleturquoise: '#AFEEEE', lightsteelblue: '#B0C4DE', powderblue: '#B0E0E6', firebrick: '#B22222', darkgoldenrod: '#B8860B', mediumorchid: '#BA55D3', rosybrown: '#BC8F8F', darkkhaki: '#BDB76B', silver: '#C0C0C0', mediumvioletred: '#C71585', indianred: '#CD5C5C', peru: '#CD853F', chocolate: '#D2691E', tan: '#D2B48C', lightgrey: '#D3D3D3', palevioletred: '#D87093', thistle: '#D8BFD8', orchid: '#DA70D6', goldenrod: '#DAA520', crimson: '#DC143C', gainsboro: '#DCDCDC', plum: '#DDA0DD', burlywood: '#DEB887', lightcyan: '#E0FFFF', lavender: '#E6E6FA', darksalmon: '#E9967A', violet: '#EE82EE', palegoldenrod: '#EEE8AA', lightcoral: '#F08080', khaki: '#F0E68C', aliceblue: '#F0F8FF', honeydew: '#F0FFF0', azure: '#F0FFFF', sandybrown: '#F4A460', wheat: '#F5DEB3', beige: '#F5F5DC', whitesmoke: '#F5F5F5', mintcream: '#F5FFFA', ghostwhite: '#F8F8FF', salmon: '#FA8072', antiquewhite: '#FAEBD7', linen: '#FAF0E6', lightgoldenrodyellow: '#FAFAD2', oldlace: '#FDF5E6', red: '#FF0000', fuchsia: '#FF00FF', magenta: '#FF00FF', deeppink: '#FF1493', orangered: '#FF4500', tomato: '#FF6347', hotpink: '#FF69B4', coral: '#FF7F50', darkorange: '#FF8C00', lightsalmon: '#FFA07A', orange: '#FFA500', lightpink: '#FFB6C1', pink: '#FFC0CB', gold: '#FFD700', peachpuff: '#FFDAB9', navajowhite: '#FFDEAD', moccasin: '#FFE4B5', bisque: '#FFE4C4', mistyrose: '#FFE4E1', blanchedalmond: '#FFEBCD', papayawhip: '#FFEFD5', lavenderblush: '#FFF0F5', seashell: '#FFF5EE', cornsilk: '#FFF8DC', lemonchiffon: '#FFFACD', floralwhite: '#FFFAF0', snow: '#FFFAFA', yellow: '#FFFF00', lightyellow: '#FFFFE0', ivory: '#FFFFF0', white: '#FFFFFF' }; - if (typeof color === 'string') { - return htmlColors[color]; - } - } - }, { - key: 'setColor', + var _nodesShapesDatabase2 = _interopRequireDefault(_nodesShapesDatabase); - /** - * Set the color of the colorPicker - * Supported formats: - * 'red' --> HTML color string - * '#ffffff' --> hex string - * 'rbg(255,255,255)' --> rgb string - * 'rgba(255,255,255,1.0)' --> rgba string - * {r:255,g:255,b:255} --> rgb object - * {r:255,g:255,b:255,a:1.0} --> rgba object - * @param color - * @param setInitial - */ - value: function setColor(color) { - var setInitial = arguments[1] === undefined ? true : arguments[1]; + var _nodesShapesDiamond = __webpack_require__(95); - if (color === 'none') { - return; - } + var _nodesShapesDiamond2 = _interopRequireDefault(_nodesShapesDiamond); - var rgba = undefined; + var _nodesShapesDot = __webpack_require__(96); - // if a html color shorthand is used, convert to hex - var htmlColor = this._isColorString(color); - if (htmlColor !== undefined) { - color = htmlColor; - } + var _nodesShapesDot2 = _interopRequireDefault(_nodesShapesDot); - // check format - if (util.isString(color) === true) { - if (util.isValidRGB(color) === true) { - var rgbaArray = color.substr(4).substr(0, color.length - 5).split(','); - rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1 }; - } else if (util.isValidRGBA(color) === true) { - var rgbaArray = color.substr(5).substr(0, color.length - 6).split(','); - rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: rgbaArray[3] }; - } else if (util.isValidHex(color) === true) { - var rgbObj = util.hexToRGB(color); - rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1 }; - } - } else { - if (color instanceof Object) { - if (color.r !== undefined && color.g !== undefined && color.b !== undefined) { - var alpha = color.a !== undefined ? color.a : '1.0'; - rgba = { r: color.r, g: color.g, b: color.b, a: alpha }; - } - } - } + var _nodesShapesEllipse = __webpack_require__(97); - // 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)); - } else { - this._setColor(rgba, setInitial); - } - } - }, { - key: 'show', + var _nodesShapesEllipse2 = _interopRequireDefault(_nodesShapesEllipse); - /** - * this shows the color picker at a location. The hue circle is constructed once and stored. - * @param x - * @param y - */ - value: function show(x, y) { - this.applied = false; - this.frame.style.display = 'block'; - this.frame.style.top = y + 'px'; - this.frame.style.left = x + 'px'; - this._generateHueCircle(); - } - }, { - key: '_hide', + var _nodesShapesIcon = __webpack_require__(98); - // ------------------------------------------ PRIVATE ----------------------------- // + var _nodesShapesIcon2 = _interopRequireDefault(_nodesShapesIcon); - /** - * Hide the picker. Is called by the cancel button. - * Optional boolean to store the previous color for easy access later on. - * @param storePrevious - * @private - */ - value: function _hide() { - var storePrevious = arguments[0] === undefined ? true : arguments[0]; + var _nodesShapesImage = __webpack_require__(99); - // store the previous color for next time; - if (storePrevious === true) { - this.previousColor = util.extend({}, this.color); - } + var _nodesShapesImage2 = _interopRequireDefault(_nodesShapesImage); - if (this.applied === true) { - this.updateCallback(this.initialColor); - } + var _nodesShapesSquare = __webpack_require__(100); - this.frame.style.display = 'none'; - } - }, { - key: '_save', + var _nodesShapesSquare2 = _interopRequireDefault(_nodesShapesSquare); + + var _nodesShapesStar = __webpack_require__(101); + + var _nodesShapesStar2 = _interopRequireDefault(_nodesShapesStar); + + var _nodesShapesText = __webpack_require__(102); + + var _nodesShapesText2 = _interopRequireDefault(_nodesShapesText); + + var _nodesShapesTriangle = __webpack_require__(103); + + var _nodesShapesTriangle2 = _interopRequireDefault(_nodesShapesTriangle); + + var _nodesShapesTriangleDown = __webpack_require__(104); + + var _nodesShapesTriangleDown2 = _interopRequireDefault(_nodesShapesTriangleDown); + + var _sharedValidator = __webpack_require__(46); + + var _sharedValidator2 = _interopRequireDefault(_sharedValidator); + + var util = __webpack_require__(1); + + /** + * @class Node + * A node. A node can be connected to other nodes via one or multiple edges. + * @param {object} options An object containing options for the node. All + * options are optional, except for the id. + * {number} id Id of the node. Required + * {string} label Text label for the node + * {number} x Horizontal position of the node + * {number} y Vertical position of the node + * {string} shape Node shape, available: + * "database", "circle", "ellipse", + * "box", "image", "text", "dot", + * "star", "triangle", "triangleDown", + * "square", "icon" + * {string} image An image url + * {string} title An title text, can be HTML + * {anytype} group A group name or number + * @param {Network.Images} imagelist A list with images. Only needed + * when the node has an image + * @param {Network.Groups} grouplist A list with groups. Needed for + * retrieving group options + * @param {Object} constants An object with default values for + * example for the color + * + */ + + var Node = (function () { + function Node(options, body, imagelist, grouplist, globalOptions) { + _classCallCheck(this, Node); + + this.options = util.bridgeObject(globalOptions); + this.body = body; + + this.edges = []; // all edges connected to this node + + // set defaults for the options + this.id = undefined; + this.imagelist = imagelist; + this.grouplist = grouplist; + + // state options + 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 + this.selected = false; + this.hover = false; + + this.labelModule = new _sharedLabel2['default'](this.body, this.options); + this.setOptions(options); + } + + _createClass(Node, [{ + key: 'attachEdge', /** - * bound to the save button. Saves and hides. - * @private + * Attach a edge to the node + * @param {Edge} edge */ - value: function _save() { - this.updateCallback(this.color); - this.applied = false; - this._hide(); + value: function attachEdge(edge) { + if (this.edges.indexOf(edge) === -1) { + this.edges.push(edge); + } } }, { - key: '_apply', + key: 'detachEdge', /** - * Bound to apply button. Saves but does not close. Is undone by the cancel button. - * @private + * Detach a edge from the node + * @param {Edge} edge */ - value: function _apply() { - this.applied = true; - this.updateCallback(this.color); - this._updatePicker(this.color); + value: function detachEdge(edge) { + var index = this.edges.indexOf(edge); + if (index != -1) { + this.edges.splice(index, 1); + } } }, { - key: '_loadLast', + key: 'togglePhysics', /** - * load the color from the previous session. - * @private + * Enable or disable the physics. + * @param status */ - value: function _loadLast() { - if (this.previousColor !== undefined) { - this.setColor(this.previousColor, false); - } else { - alert('There is no last color to load...'); - } + value: function togglePhysics(status) { + this.options.physics = status; } }, { - key: '_setColor', + key: 'setOptions', /** - * set the color, place the picker - * @param rgba - * @param setInitial - * @private + * Set or overwrite options for the node + * @param {Object} options an object with options + * @param {Object} constants and object with default, global options */ - value: function _setColor(rgba) { - var setInitial = arguments[1] === undefined ? true : arguments[1]; + value: function setOptions(options) { + if (!options) { + return; + } + // basic options + if (options.id !== undefined) { + this.id = options.id; + } - // store the initial color - if (setInitial === true) { - this.initialColor = util.extend({}, rgba); + if (this.id === undefined) { + throw 'Node must have an id'; } - this.color = rgba; - var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + // set these options locally + if (options.x !== undefined) { + this.x = parseInt(options.x);this.predefinedPosition = true; + } + if (options.y !== undefined) { + this.y = parseInt(options.y);this.predefinedPosition = true; + } + if (options.size !== undefined) { + this.baseSize = options.size; + } + if (options.value !== undefined) { + options.value = parseInt(options.value); + } - var angleConvert = 2 * Math.PI; - var radius = this.r * hsv.s; - var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h); - var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h); + // copy group options + if (typeof options.group === 'number' || typeof options.group === 'string' && options.group != '') { + var groupObj = this.grouplist.get(options.group); + util.deepExtend(this.options, groupObj); + // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case. + this.options.color = util.parseColor(this.options.color); + } - this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + 'px'; - this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + 'px'; + // this transforms all shorthands into fully defined options + Node.parseOptions(this.options, options, true); - this._updatePicker(rgba); + // load the images + if (this.options.image !== undefined && this.options.image != '') { + if (this.imagelist) { + this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); + } else { + throw 'No imagelist provided'; + } + } + + this.updateShape(); + this.updateLabelModule(); + + // reset the size of the node, this can be changed + this._reset(); } }, { - key: '_setOpacity', + key: 'updateLabelModule', + value: function updateLabelModule() { + if (this.options.label === undefined || this.options.label === null) { + this.options.label = ''; + } + this.labelModule.setOptions(this.options, true); + if (this.labelModule.baseSize !== undefined) { + this.baseFontSize = this.labelModule.baseSize; + } + } + }, { + key: 'updateShape', + value: function updateShape() { + // choose draw method depending on the shape + switch (this.options.shape) { + case 'box': + this.shape = new _nodesShapesBox2['default'](this.options, this.body, this.labelModule); + break; + case 'circle': + this.shape = new _nodesShapesCircle2['default'](this.options, this.body, this.labelModule); + break; + case 'circularImage': + this.shape = new _nodesShapesCircularImage2['default'](this.options, this.body, this.labelModule, this.imageObj); + break; + case 'database': + this.shape = new _nodesShapesDatabase2['default'](this.options, this.body, this.labelModule); + break; + case 'diamond': + this.shape = new _nodesShapesDiamond2['default'](this.options, this.body, this.labelModule); + break; + case 'dot': + this.shape = new _nodesShapesDot2['default'](this.options, this.body, this.labelModule); + break; + case 'ellipse': + this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule); + break; + case 'icon': + this.shape = new _nodesShapesIcon2['default'](this.options, this.body, this.labelModule); + break; + case 'image': + this.shape = new _nodesShapesImage2['default'](this.options, this.body, this.labelModule, this.imageObj); + break; + case 'square': + this.shape = new _nodesShapesSquare2['default'](this.options, this.body, this.labelModule); + break; + case 'star': + this.shape = new _nodesShapesStar2['default'](this.options, this.body, this.labelModule); + break; + case 'text': + this.shape = new _nodesShapesText2['default'](this.options, this.body, this.labelModule); + break; + case 'triangle': + this.shape = new _nodesShapesTriangle2['default'](this.options, this.body, this.labelModule); + break; + case 'triangleDown': + this.shape = new _nodesShapesTriangleDown2['default'](this.options, this.body, this.labelModule); + break; + default: + this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule); + break; + } + this._reset(); + } + }, { + key: 'select', /** - * bound to opacity control - * @param value - * @private + * select this node */ - value: function _setOpacity(value) { - this.color.a = value / 100; - this._updatePicker(this.color); + value: function select() { + this.selected = true; + this._reset(); } }, { - key: '_setBrightness', + key: 'unselect', /** - * bound to brightness control - * @param value - * @private + * unselect this node */ - value: function _setBrightness(value) { - var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); - hsv.v = value / 100; - var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); - rgba['a'] = this.color.a; - this.color = rgba; - this._updatePicker(); + value: function unselect() { + this.selected = false; + this._reset(); } }, { - key: '_updatePicker', + key: '_reset', /** - * update the colorpicker. A black circle overlays the hue circle to mimic the brightness decreasing. - * @param rgba + * Reset the calculated size of the node, forces it to recalculate its size * @private */ - value: function _updatePicker() { - var rgba = arguments[0] === undefined ? this.color : arguments[0]; + value: function _reset() { + this.shape.width = undefined; + this.shape.height = undefined; + } + }, { + key: 'getTitle', - var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); - var ctx = this.colorPickerCanvas.getContext('2d'); - if (this.pixelRation === undefined) { - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); - } - ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + /** + * get the title of this node. + * @return {string} title The title of the node, or undefined when no title + * has been set. + */ + value: function getTitle() { + return this.options.title; + } + }, { + key: 'distanceToBorder', - // clear the canvas - var w = this.colorPickerCanvas.clientWidth; - var h = this.colorPickerCanvas.clientHeight; - ctx.clearRect(0, 0, w, h); + /** + * Calculate the distance to the border of the Node + * @param {CanvasRenderingContext2D} ctx + * @param {Number} angle Angle in radians + * @returns {number} distance Distance to the border in pixels + */ + value: function distanceToBorder(ctx, angle) { + return this.shape.distanceToBorder(ctx, angle); + } + }, { + key: 'isFixed', - ctx.putImageData(this.hueCircle, 0, 0); - ctx.fillStyle = 'rgba(0,0,0,' + (1 - hsv.v) + ')'; - ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); - ctx.fill(); + /** + * Check if this node has a fixed x and y position + * @return {boolean} true if fixed, false if not + */ + value: function isFixed() { + return this.options.fixed.x && this.options.fixed.y; + } + }, { + key: 'isSelected', - this.brightnessRange.value = 100 * hsv.v; - this.opacityRange.value = 100 * rgba.a; + /** + * check if this node is selecte + * @return {boolean} selected True if node is selected, else false + */ + value: function isSelected() { + return this.selected; + } + }, { + key: 'getValue', - 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 + ')'; + /** + * Retrieve the value of the node. Can be undefined + * @return {Number} value + */ + value: function getValue() { + return this.options.value; } }, { - key: '_setSize', + key: 'setValueRange', /** - * used by create to set the size of the canvas. - * @private + * Adjust the value range of the node. The node will adjust it's size + * based on its value. + * @param {Number} min + * @param {Number} max */ - value: function _setSize() { - this.colorPickerCanvas.style.width = '100%'; - this.colorPickerCanvas.style.height = '100%'; + value: function setValueRange(min, max, total) { + if (this.options.value !== undefined) { + var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); + var sizeDiff = this.options.scaling.max - this.options.scaling.min; + if (this.options.scaling.label.enabled === true) { + var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; + this.options.font.size = this.options.scaling.label.min + scale * fontDiff; + } + this.options.size = this.options.scaling.min + scale * sizeDiff; + } else { + this.options.size = this.baseSize; + this.options.font.size = this.baseFontSize; + } + } + }, { + key: 'draw', - this.colorPickerCanvas.width = 289 * this.pixelRatio; - this.colorPickerCanvas.height = 289 * this.pixelRatio; + /** + * Draw this node in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + */ + value: function draw(ctx) { + this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); } }, { - key: '_create', + key: 'updateBoundingBox', /** - * create all dom elements - * TODO: cleanup, lots of similar dom elements - * @private + * Update the bounding box of the shape */ - value: function _create() { - this.frame = document.createElement('div'); - this.frame.className = 'vis-color-picker'; + value: function updateBoundingBox() { + this.shape.updateBoundingBox(this.x, this.y); + } + }, { + key: 'resize', - this.colorPickerDiv = document.createElement('div'); - this.colorPickerSelector = document.createElement('div'); - this.colorPickerSelector.className = 'vis-selector'; - this.colorPickerDiv.appendChild(this.colorPickerSelector); + /** + * 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 + */ + value: function resize(ctx) { + this.shape.resize(ctx); + } + }, { + key: 'isOverlappingWith', - this.colorPickerCanvas = document.createElement('canvas'); - this.colorPickerDiv.appendChild(this.colorPickerCanvas); + /** + * 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 + */ + 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', - if (!this.colorPickerCanvas.getContext) { - var noCanvas = document.createElement('DIV'); - noCanvas.style.color = 'red'; - noCanvas.style.fontWeight = 'bold'; - noCanvas.style.padding = '10px'; - noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; - this.colorPickerCanvas.appendChild(noCanvas); - } else { - var ctx = this.colorPickerCanvas.getContext('2d'); - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); + /** + * 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 + */ + 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.colorPickerCanvas.getContext('2d').setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + /** + * 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[2] === undefined ? false : arguments[2]; + + var fields = ['color', 'font', 'fixed', 'shadow']; + util.selectiveNotDeepExtend(fields, parentOptions, newOptions, allowDeletion); + + // merge the shadow options into the parent. + util.mergeOptions(parentOptions, newOptions, 'shadow'); + + // individual shape newOptions + if (newOptions.color !== undefined && newOptions.color !== null) { + var parsedColor = util.parseColor(newOptions.color); + util.fillIfDefined(parentOptions.color, parsedColor); + } else if (allowDeletion === true && newOptions.color === null) { + parentOptions.color = undefined; + delete parentOptions.color; } - this.colorPickerDiv.className = 'vis-color'; + // 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; + } else { + if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') { + parentOptions.fixed.x = newOptions.fixed.x; + } + if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') { + parentOptions.fixed.y = newOptions.fixed.y; + } + } + } - this.opacityDiv = document.createElement('div'); - this.opacityDiv.className = 'vis-opacity'; + // handle the font options + if (newOptions.font !== undefined) { + _sharedLabel2['default'].parseOptions(parentOptions.font, newOptions); + } - this.brightnessDiv = document.createElement('div'); - this.brightnessDiv.className = 'vis-brightness'; + // handle the scaling options, specifically the label part + if (newOptions.scaling !== undefined) { + util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label'); + } + } + }]); - this.arrowDiv = document.createElement('div'); - this.arrowDiv.className = 'vis-arrow'; + return Node; + })(); - this.opacityRange = document.createElement('input'); - this.opacityRange.type = 'range'; - this.opacityRange.min = '0'; - this.opacityRange.max = '100'; - this.opacityRange.value = '100'; - this.opacityRange.className = 'vis-range'; + exports['default'] = Node; + module.exports = exports['default']; - this.brightnessRange = document.createElement('input'); - this.brightnessRange.type = 'range'; - this.brightnessRange.min = '0'; - this.brightnessRange.max = '100'; - this.brightnessRange.value = '100'; - this.brightnessRange.className = 'vis-range'; +/***/ }, +/* 74 */ +/***/ function(module, exports, __webpack_require__) { - this.opacityDiv.appendChild(this.opacityRange); - this.brightnessDiv.appendChild(this.brightnessRange); + 'use strict'; - var me = this; - this.opacityRange.onchange = function () { - me._setOpacity(this.value); - }; - this.opacityRange.oninput = function () { - me._setOpacity(this.value); - }; - this.brightnessRange.onchange = function () { - me._setBrightness(this.value); - }; - this.brightnessRange.oninput = function () { - me._setBrightness(this.value); - }; + Object.defineProperty(exports, '__esModule', { + value: true + }); - this.brightnessLabel = document.createElement('div'); - this.brightnessLabel.className = 'vis-label vis-brightness'; - this.brightnessLabel.innerHTML = 'brightness:'; + 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; }; })(); - this.opacityLabel = document.createElement('div'); - this.opacityLabel.className = 'vis-label vis-opacity'; - this.opacityLabel.innerHTML = 'opacity:'; + function _slicedToArray(arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } } - this.newColorDiv = document.createElement('div'); - this.newColorDiv.className = 'vis-new-color'; - this.newColorDiv.innerHTML = 'new'; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - this.initialColorDiv = document.createElement('div'); - this.initialColorDiv.className = 'vis-initial-color'; - this.initialColorDiv.innerHTML = 'initial'; + var util = __webpack_require__(1); - this.cancelButton = document.createElement('div'); - this.cancelButton.className = 'vis-button vis-cancel'; - this.cancelButton.innerHTML = 'cancel'; - this.cancelButton.onclick = this._hide.bind(this, false); + var Label = (function () { + function Label(body, options) { + _classCallCheck(this, Label); - this.applyButton = document.createElement('div'); - this.applyButton.className = 'vis-button vis-apply'; - this.applyButton.innerHTML = 'apply'; - this.applyButton.onclick = this._apply.bind(this); + this.body = body; - this.saveButton = document.createElement('div'); - this.saveButton.className = 'vis-button vis-save'; - this.saveButton.innerHTML = 'save'; - this.saveButton.onclick = this._save.bind(this); + this.baseSize = undefined; + this.setOptions(options); + this.size = { top: 0, left: 0, width: 0, height: 0, yLine: 0 }; // could be cached + } - this.loadButton = document.createElement('div'); - this.loadButton.className = 'vis-button vis-load'; - this.loadButton.innerHTML = 'load last'; - this.loadButton.onclick = this._loadLast.bind(this); + _createClass(Label, [{ + key: 'setOptions', + value: function setOptions(options) { + var allowDeletion = arguments[1] === undefined ? false : arguments[1]; - this.frame.appendChild(this.colorPickerDiv); - this.frame.appendChild(this.arrowDiv); - this.frame.appendChild(this.brightnessLabel); - this.frame.appendChild(this.brightnessDiv); - this.frame.appendChild(this.opacityLabel); - this.frame.appendChild(this.opacityDiv); - this.frame.appendChild(this.newColorDiv); - this.frame.appendChild(this.initialColorDiv); + this.options = options; - this.frame.appendChild(this.cancelButton); - this.frame.appendChild(this.applyButton); - this.frame.appendChild(this.saveButton); - this.frame.appendChild(this.loadButton); + if (options.label !== undefined) { + this.labelDirty = true; + } + + if (options.font !== undefined) { + Label.parseOptions(this.options.font, options, allowDeletion); + if (typeof options.font === 'string') { + this.baseSize = this.options.font.size; + } else if (typeof options.font === 'object') { + if (options.font.size !== undefined) { + this.baseSize = options.font.size; + } + } + } } }, { - key: '_bindHammer', + key: 'draw', /** - * bind hammer to the color picker - * @private + * Main function. This is called from anything that wants to draw a label. + * @param ctx + * @param x + * @param y + * @param selected + * @param baseline */ - value: function _bindHammer() { - var _this = this; + value: function draw(ctx, x, y, selected) { + var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; - this.drag = {}; - this.pinch = {}; - this.hammer = new Hammer(this.colorPickerCanvas); - this.hammer.get('pinch').set({ enable: true }); + // if no label, return + if (this.options.label === undefined) return; - hammerUtil.onTouch(this.hammer, function (event) { - _this._moveSelector(event); - }); - this.hammer.on('tap', function (event) { - _this._moveSelector(event); - }); - this.hammer.on('panstart', function (event) { - _this._moveSelector(event); - }); - this.hammer.on('panmove', function (event) { - _this._moveSelector(event); - }); - this.hammer.on('panend', function (event) { - _this._moveSelector(event); - }); + // check if we have to render the label + var viewFontSize = this.options.font.size * this.body.view.scale; + if (this.options.label && viewFontSize < this.options.scaling.label.drawThreshold - 1) return; + + // update the size cache if required + this.calculateLabelSize(ctx, selected, x, y, baseline); + + // create the fontfill background + this._drawBackground(ctx); + // draw text + this._drawText(ctx, selected, x, y, baseline); } }, { - key: '_generateHueCircle', + key: '_drawBackground', /** - * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown. + * Draws the label background + * @param {CanvasRenderingContext2D} ctx * @private */ - value: function _generateHueCircle() { - if (this.generated === false) { - var ctx = this.colorPickerCanvas.getContext('2d'); - if (this.pixelRation === undefined) { - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); + value: function _drawBackground(ctx) { + if (this.options.font.background !== undefined && this.options.font.background !== 'none') { + ctx.fillStyle = this.options.font.background; + + var lineMargin = 2; + + switch (this.options.font.align) { + case 'middle': + ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); + break; + case 'top': + ctx.fillRect(-this.size.width * 0.5, -(this.size.height + lineMargin), this.size.width, this.size.height); + break; + case 'bottom': + ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height); + break; + default: + ctx.fillRect(this.size.left, this.size.top - 0.5 * lineMargin, this.size.width, this.size.height); + break; } - ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + } + } + }, { + key: '_drawText', - // clear the canvas - var w = this.colorPickerCanvas.clientWidth; - var h = this.colorPickerCanvas.clientHeight; - ctx.clearRect(0, 0, w, h); + /** + * + * @param ctx + * @param x + * @param baseline + * @private + */ + value: function _drawText(ctx, selected, x, y) { + var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; - // draw hue circle - var x = undefined, - y = undefined, - hue = undefined, - sat = undefined; - this.centerCoordinates = { x: w * 0.5, y: h * 0.5 }; - this.r = 0.49 * w; - var angleConvert = 2 * Math.PI / 360; - var hfac = 1 / 360; - var sfac = 1 / this.r; - var rgb = undefined; - for (hue = 0; hue < 360; hue++) { - for (sat = 0; sat < this.r; sat++) { - x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue); - y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue); - rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1); - ctx.fillStyle = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')'; - ctx.fillRect(x - 0.5, y - 0.5, 2, 2); - } + var fontSize = this.options.font.size; + var viewFontSize = fontSize * this.body.view.scale; + // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel) + if (viewFontSize >= this.options.scaling.label.maxVisible) { + fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale; + } + + var yLine = this.size.yLine; + + var _getColor = this._getColor(viewFontSize); + + var _getColor2 = _slicedToArray(_getColor, 2); + + var fontColor = _getColor2[0]; + var strokeColor = _getColor2[1]; + + var _setAlignment = this._setAlignment(ctx, x, yLine, baseline); + + var _setAlignment2 = _slicedToArray(_setAlignment, 2); + + x = _setAlignment2[0]; + yLine = _setAlignment2[1]; + + // configure context for drawing the text + ctx.font = (selected ? 'bold ' : '') + fontSize + 'px ' + this.options.font.face; + ctx.fillStyle = fontColor; + ctx.textAlign = 'center'; + + // set the strokeWidth + if (this.options.font.strokeWidth > 0) { + ctx.lineWidth = this.options.font.strokeWidth; + ctx.strokeStyle = strokeColor; + ctx.lineJoin = 'round'; + } + + // draw the text + for (var i = 0; i < this.lineCount; i++) { + if (this.options.font.strokeWidth > 0) { + ctx.strokeText(this.lines[i], x, yLine); } - ctx.strokeStyle = 'rgba(0,0,0,1)'; - ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); - ctx.stroke(); + ctx.fillText(this.lines[i], x, yLine); + yLine += fontSize; + } + } + }, { + key: '_setAlignment', + value: function _setAlignment(ctx, x, yLine, baseline) { + // check for label alignment (for edges) + // TODO: make alignment for nodes + if (this.options.font.align !== 'horizontal') { + x = 0; + yLine = 0; - this.hueCircle = ctx.getImageData(0, 0, w, h); + var lineMargin = 2; + if (this.options.font.align === 'top') { + ctx.textBaseline = 'alphabetic'; + yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers + } else if (this.options.font.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'; + } + } else { + ctx.textBaseline = baseline; } - this.generated = true; + + return [x, yLine]; } }, { - key: '_moveSelector', + key: '_getColor', /** - * move the selector. This is called by hammer functions. + * fade in when relative scale is between threshold and threshold - 1. + * If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here. * - * @param event + * @param viewFontSize + * @returns {*[]} * @private */ - value: function _moveSelector(event) { - var rect = this.colorPickerDiv.getBoundingClientRect(); - var left = event.center.x - rect.left; - var top = event.center.y - rect.top; + value: function _getColor(viewFontSize) { + var fontColor = this.options.font.color || '#000000'; + var strokeColor = this.options.font.strokeColor || '#ffffff'; + if (viewFontSize <= this.options.scaling.label.drawThreshold) { + var opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - viewFontSize))); + fontColor = util.overrideOpacity(fontColor, opacity); + strokeColor = util.overrideOpacity(strokeColor, opacity); + } + return [fontColor, strokeColor]; + } + }, { + key: 'getTextSize', - var centerY = 0.5 * this.colorPickerDiv.clientHeight; - var centerX = 0.5 * this.colorPickerDiv.clientWidth; + /** + * + * @param ctx + * @param selected + * @returns {{width: number, height: number}} + */ + value: function getTextSize(ctx) { + var selected = arguments[1] === undefined ? false : arguments[1]; - var x = left - centerX; - var y = top - centerY; + var size = { + width: this._processLabel(ctx, selected), + height: this.options.font.size * this.lineCount, + lineCount: this.lineCount + }; + return size; + } + }, { + key: 'calculateLabelSize', - var angle = Math.atan2(x, y); - var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX); + /** + * + * @param ctx + * @param selected + * @param x + * @param y + * @param baseline + */ + value: function calculateLabelSize(ctx, selected) { + var x = arguments[2] === undefined ? 0 : arguments[2]; + var y = arguments[3] === undefined ? 0 : arguments[3]; + var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; - var newTop = Math.cos(angle) * radius + centerY; - var newLeft = Math.sin(angle) * radius + centerX; + if (this.labelDirty === true) { + this.size.width = this._processLabel(ctx, selected); + } + this.size.height = this.options.font.size * this.lineCount; + 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.options.font.size; + if (baseline === 'hanging') { + this.size.top += 0.5 * this.options.font.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 + } - this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + 'px'; - this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + 'px'; + this.labelDirty = false; + } + }, { + key: '_processLabel', - // set color - var h = angle / (2 * Math.PI); - h = h < 0 ? h + 1 : h; - var s = radius / this.r; - var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); - hsv.h = h; - hsv.s = s; - var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); - rgba['a'] = this.color.a; - this.color = rgba; + /** + * This calculates the width as well as explodes the label string and calculates the amount of lines. + * @param ctx + * @param selected + * @returns {number} + * @private + */ + value: function _processLabel(ctx, selected) { + var width = 0; + var lines = ['']; + var lineCount = 0; + if (this.options.label !== undefined) { + lines = String(this.options.label).split('\n'); + lineCount = lines.length; + ctx.font = (selected ? 'bold ' : '') + this.options.font.size + 'px ' + this.options.font.face; + width = ctx.measureText(lines[0]).width; + for (var i = 1; i < lineCount; i++) { + var lineWidth = ctx.measureText(lines[i]).width; + width = lineWidth > width ? lineWidth : width; + } + } + this.lines = lines; + this.lineCount = lineCount; - // update previews - 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 + ')'; + return width; + } + }], [{ + key: 'parseOptions', + value: function parseOptions(parentOptions, newOptions) { + var allowDeletion = arguments[2] === undefined ? false : arguments[2]; + + if (typeof newOptions.font === 'string') { + 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') { + util.fillIfDefined(parentOptions, newOptions.font, allowDeletion); + } + parentOptions.size = Number(parentOptions.size); } }]); - return ColorPicker; + return Label; })(); - exports['default'] = ColorPicker; + exports['default'] = Label; module.exports = exports['default']; /***/ }, -/* 74 */ +/* 75 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -34680,347 +35059,239 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _sharedLabel = __webpack_require__(75); + var _sharedLabel = __webpack_require__(74); var _sharedLabel2 = _interopRequireDefault(_sharedLabel); - var _nodesShapesBox = __webpack_require__(90); - - var _nodesShapesBox2 = _interopRequireDefault(_nodesShapesBox); - - var _nodesShapesCircle = __webpack_require__(91); - - var _nodesShapesCircle2 = _interopRequireDefault(_nodesShapesCircle); - - var _nodesShapesCircularImage = __webpack_require__(92); - - var _nodesShapesCircularImage2 = _interopRequireDefault(_nodesShapesCircularImage); - - var _nodesShapesDatabase = __webpack_require__(93); - - var _nodesShapesDatabase2 = _interopRequireDefault(_nodesShapesDatabase); - - var _nodesShapesDiamond = __webpack_require__(94); - - var _nodesShapesDiamond2 = _interopRequireDefault(_nodesShapesDiamond); - - var _nodesShapesDot = __webpack_require__(95); - - var _nodesShapesDot2 = _interopRequireDefault(_nodesShapesDot); - - var _nodesShapesEllipse = __webpack_require__(96); - - var _nodesShapesEllipse2 = _interopRequireDefault(_nodesShapesEllipse); - - var _nodesShapesIcon = __webpack_require__(97); - - var _nodesShapesIcon2 = _interopRequireDefault(_nodesShapesIcon); - - var _nodesShapesImage = __webpack_require__(98); - - var _nodesShapesImage2 = _interopRequireDefault(_nodesShapesImage); - - var _nodesShapesSquare = __webpack_require__(99); - - var _nodesShapesSquare2 = _interopRequireDefault(_nodesShapesSquare); - - var _nodesShapesStar = __webpack_require__(100); - - var _nodesShapesStar2 = _interopRequireDefault(_nodesShapesStar); - - var _nodesShapesText = __webpack_require__(101); - - var _nodesShapesText2 = _interopRequireDefault(_nodesShapesText); + var _edgesBezierEdgeDynamic = __webpack_require__(105); - var _nodesShapesTriangle = __webpack_require__(102); - - var _nodesShapesTriangle2 = _interopRequireDefault(_nodesShapesTriangle); + var _edgesBezierEdgeDynamic2 = _interopRequireDefault(_edgesBezierEdgeDynamic); - var _nodesShapesTriangleDown = __webpack_require__(103); + var _edgesBezierEdgeStatic = __webpack_require__(106); - var _nodesShapesTriangleDown2 = _interopRequireDefault(_nodesShapesTriangleDown); + var _edgesBezierEdgeStatic2 = _interopRequireDefault(_edgesBezierEdgeStatic); - var _sharedValidator = __webpack_require__(45); + var _edgesStraightEdge = __webpack_require__(107); - var _sharedValidator2 = _interopRequireDefault(_sharedValidator); + var _edgesStraightEdge2 = _interopRequireDefault(_edgesStraightEdge); var util = __webpack_require__(1); /** - * @class Node - * A node. A node can be connected to other nodes via one or multiple edges. - * @param {object} options An object containing options for the node. All - * options are optional, except for the id. - * {number} id Id of the node. Required - * {string} label Text label for the node - * {number} x Horizontal position of the node - * {number} y Vertical position of the node - * {string} shape Node shape, available: - * "database", "circle", "ellipse", - * "box", "image", "text", "dot", - * "star", "triangle", "triangleDown", - * "square", "icon" - * {string} image An image url - * {string} title An title text, can be HTML - * {anytype} group A group name or number - * @param {Network.Images} imagelist A list with images. Only needed - * when the node has an image - * @param {Network.Groups} grouplist A list with groups. Needed for - * retrieving group options - * @param {Object} constants An object with default values for - * example for the color + * @class Edge * + * A edge connects two nodes + * @param {Object} properties Object with options. Must contain + * At least options from and to. + * Available options: from (number), + * to (number), label (string, color (string), + * width (number), style (string), + * length (number), title (string) + * @param {Network} network A Network object, used to find and edge to + * nodes. + * @param {Object} constants An object with default values for + * example for the color */ - var Node = (function () { - function Node(options, body, imagelist, grouplist, globalOptions) { - _classCallCheck(this, Node); + var Edge = (function () { + function Edge(options, body, globalOptions) { + _classCallCheck(this, Edge); + if (body === undefined) { + throw 'No body provided'; + } this.options = util.bridgeObject(globalOptions); this.body = body; - this.edges = []; // all edges connected to this node - - // set defaults for the options + // initialize variables this.id = undefined; - this.imagelist = imagelist; - this.grouplist = grouplist; - - // state options - 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 + this.fromId = undefined; + this.toId = undefined; this.selected = false; this.hover = false; + this.labelDirty = true; + this.colorDirty = true; - this.labelModule = new _sharedLabel2['default'](this.body, this.options); - this.setOptions(options); - } + this.baseWidth = this.options.width; + this.baseFontSize = this.options.font.size; - _createClass(Node, [{ - key: 'attachEdge', + this.from = undefined; // a node + this.to = undefined; // a node - /** - * 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', + this.edgeType = undefined; - /** - * Detach a edge from the node - * @param {Edge} edge - */ - value: function detachEdge(edge) { - var index = this.edges.indexOf(edge); - if (index != -1) { - this.edges.splice(index, 1); - } - } - }, { - key: 'togglePhysics', + this.connected = false; - /** - * Enable or disable the physics. - * @param status - */ - value: function togglePhysics(status) { - this.options.physics = status; - } - }, { + this.labelModule = new _sharedLabel2['default'](this.body, this.options); + + this.setOptions(options); + } + + _createClass(Edge, [{ 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 + * Set or overwrite options for the edge + * @param {Object} options an object with options + * @param doNotEmit */ value: function setOptions(options) { if (!options) { return; } - // basic options + this.colorDirty = true; + + Edge.parseOptions(this.options, options, true); + if (options.id !== undefined) { this.id = options.id; } - - if (this.id === undefined) { - throw 'Node must have an id'; - } - - if (options.x !== undefined) { - this.x = parseInt(options.x);this.predefinedPosition = true; + if (options.from !== undefined) { + this.fromId = options.from; } - if (options.y !== undefined) { - this.y = parseInt(options.y);this.predefinedPosition = true; + if (options.to !== undefined) { + this.toId = options.to; } - if (options.size !== undefined) { - this.baseSize = options.size; + if (options.title !== undefined) { + this.title = options.title; } if (options.value !== undefined) { options.value = parseInt(options.value); } - // this transforms all shorthands into fully defined options - Node.parseOptions(this.options, options, true); + // A node is connected when it has a from and to node that both exist in the network.body.nodes. + this.connect(); - // copy group options - if (typeof options.group === 'number' || typeof options.group === 'string' && options.group != '') { - var groupObj = this.grouplist.get(options.group); - util.deepExtend(this.options, groupObj); - // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case. - this.options.color = util.parseColor(this.options.color); - } + // update label Module + this.updateLabelModule(); - // load the images - if (this.options.image !== undefined && this.options.image != '') { - if (this.imagelist) { - this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); - } else { - throw 'No imagelist provided'; - } - } + var dataChanged = this.updateEdgeType(); - this.updateShape(); - this.updateLabelModule(); + // if anything has been updates, reset the selection width and the hover width + this._setInteractionWidths(); - // reset the size of the node, this can be changed - this._reset(); + return dataChanged; } }, { key: 'updateLabelModule', + + /** + * update the options in the label module + */ value: function updateLabelModule() { - if (this.options.label === undefined || this.options.label === null) { - this.options.label = ''; - } this.labelModule.setOptions(this.options, true); if (this.labelModule.baseSize !== undefined) { this.baseFontSize = this.labelModule.baseSize; } } }, { - key: 'updateShape', - value: function updateShape() { - // choose draw method depending on the shape - switch (this.options.shape) { - case 'box': - this.shape = new _nodesShapesBox2['default'](this.options, this.body, this.labelModule); - break; - case 'circle': - this.shape = new _nodesShapesCircle2['default'](this.options, this.body, this.labelModule); - break; - case 'circularImage': - this.shape = new _nodesShapesCircularImage2['default'](this.options, this.body, this.labelModule, this.imageObj); - break; - case 'database': - this.shape = new _nodesShapesDatabase2['default'](this.options, this.body, this.labelModule); - break; - case 'diamond': - this.shape = new _nodesShapesDiamond2['default'](this.options, this.body, this.labelModule); - break; - case 'dot': - this.shape = new _nodesShapesDot2['default'](this.options, this.body, this.labelModule); - break; - case 'ellipse': - this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule); - break; - case 'icon': - this.shape = new _nodesShapesIcon2['default'](this.options, this.body, this.labelModule); - break; - case 'image': - this.shape = new _nodesShapesImage2['default'](this.options, this.body, this.labelModule, this.imageObj); - break; - case 'square': - this.shape = new _nodesShapesSquare2['default'](this.options, this.body, this.labelModule); - break; - case 'star': - this.shape = new _nodesShapesStar2['default'](this.options, this.body, this.labelModule); - break; - case 'text': - this.shape = new _nodesShapesText2['default'](this.options, this.body, this.labelModule); - break; - case 'triangle': - this.shape = new _nodesShapesTriangle2['default'](this.options, this.body, this.labelModule); - break; - case 'triangleDown': - this.shape = new _nodesShapesTriangleDown2['default'](this.options, this.body, this.labelModule); - break; - default: - this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule); - break; - } - this._reset(); - } - }, { - key: 'select', + key: 'updateEdgeType', /** - * select this node + * update the edge type, set the options + * @returns {boolean} */ - value: function select() { - this.selected = true; - this._reset(); - } - }, { - key: 'unselect', + value: function updateEdgeType() { + var dataChanged = false; + var changeInType = true; + if (this.edgeType !== undefined) { + if (this.edgeType instanceof _edgesBezierEdgeDynamic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type === 'dynamic') { + changeInType = false; + } + if (this.edgeType instanceof _edgesBezierEdgeStatic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type !== 'dynamic') { + changeInType = false; + } + if (this.edgeType instanceof _edgesStraightEdge2['default'] && this.options.smooth.enabled === false) { + changeInType = false; + } - /** - * unselect this node - */ - value: function unselect() { - this.selected = false; - this._reset(); + if (changeInType === true) { + dataChanged = this.edgeType.cleanup(); + } + } + + if (changeInType === true) { + if (this.options.smooth.enabled === true) { + if (this.options.smooth.type === 'dynamic') { + dataChanged = true; + this.edgeType = new _edgesBezierEdgeDynamic2['default'](this.options, this.body, this.labelModule); + } else { + this.edgeType = new _edgesBezierEdgeStatic2['default'](this.options, this.body, this.labelModule); + } + } else { + this.edgeType = new _edgesStraightEdge2['default'](this.options, this.body, this.labelModule); + } + } else { + // if nothing changes, we just set the options. + this.edgeType.setOptions(this.options); + } + + return dataChanged; } }, { - key: '_reset', + key: 'togglePhysics', /** - * Reset the calculated size of the node, forces it to recalculate its size - * @private + * Enable or disable the physics. + * @param status */ - value: function _reset() { - this.shape.width = undefined; - this.shape.height = undefined; + value: function togglePhysics(status) { + this.options.physics = status; + this.edgeType.togglePhysics(status); } }, { - key: 'getTitle', + key: 'connect', /** - * get the title of this node. - * @return {string} title The title of the node, or undefined when no title - * has been set. + * Connect an edge to its nodes */ - value: function getTitle() { - return this.options.title; + value: function connect() { + this.disconnect(); + + this.from = this.body.nodes[this.fromId] || undefined; + this.to = this.body.nodes[this.toId] || undefined; + this.connected = this.from !== undefined && this.to !== undefined; + + if (this.connected === true) { + this.from.attachEdge(this); + this.to.attachEdge(this); + } else { + if (this.from) { + this.from.detachEdge(this); + } + if (this.to) { + this.to.detachEdge(this); + } + } } }, { - key: 'distanceToBorder', + key: 'disconnect', /** - * Calculate the distance to the border of the Node - * @param {CanvasRenderingContext2D} ctx - * @param {Number} angle Angle in radians - * @returns {number} distance Distance to the border in pixels + * Disconnect an edge from its nodes */ - value: function distanceToBorder(ctx, angle) { - return this.shape.distanceToBorder(ctx, angle); + value: function disconnect() { + if (this.from) { + this.from.detachEdge(this); + this.from = undefined; + } + if (this.to) { + this.to.detachEdge(this); + this.to = undefined; + } + + this.connected = false; } }, { - key: 'isFixed', + key: 'getTitle', /** - * Check if this node has a fixed x and y position - * @return {boolean} true if fixed, false if not + * get the title of this edge. + * @return {string} title The title of the edge, or undefined when no title + * has been set. */ - value: function isFixed() { - return this.options.fixed.x && this.options.fixed.y; + value: function getTitle() { + return this.title; } }, { key: 'isSelected', @@ -35036,7 +35307,7 @@ return /******/ (function(modules) { // webpackBootstrap key: 'getValue', /** - * Retrieve the value of the node. Can be undefined + * Retrieve the value of the edge. Can be undefined * @return {Number} value */ value: function getValue() { @@ -35046,1010 +35317,1198 @@ return /******/ (function(modules) { // webpackBootstrap key: 'setValueRange', /** - * Adjust the value range of the node. The node will adjust it's size + * Adjust the value range of the edge. The edge will adjust it's width * based on its value. * @param {Number} min * @param {Number} max + * @param total */ value: function setValueRange(min, max, total) { if (this.options.value !== undefined) { var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); - var sizeDiff = this.options.scaling.max - this.options.scaling.min; + var widthDiff = this.options.scaling.max - this.options.scaling.min; if (this.options.scaling.label.enabled === true) { var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; this.options.font.size = this.options.scaling.label.min + scale * fontDiff; } - this.options.size = this.options.scaling.min + scale * sizeDiff; + this.options.width = this.options.scaling.min + scale * widthDiff; } else { - this.options.size = this.baseSize; + this.options.width = this.baseWidth; this.options.font.size = this.baseFontSize; } + + this._setInteractionWidths(); + } + }, { + key: '_setInteractionWidths', + value: function _setInteractionWidths() { + if (typeof this.options.hoverWidth === 'function') { + this.edgeType.hoverWidth = this.options.hoverWidth(this.options.width); + } else { + this.edgeType.hoverWidth = this.options.hoverWidth + this.options.width; + } + + if (typeof this.options.selectionWidth === 'function') { + this.edgeType.selectionWidth = this.options.selectionWidth(this.options.width); + } else { + this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width; + } } }, { key: 'draw', /** - * Draw this node in the given canvas + * Redraw a edge + * Draw this edge in the given canvas * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * @param {CanvasRenderingContext2D} ctx */ value: function draw(ctx) { - this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); + var via = this.edgeType.drawLine(ctx, this.selected, this.hover); + this.drawArrows(ctx, via); + this.drawLabel(ctx, via); } }, { - key: 'updateBoundingBox', - - /** - * Update the bounding box of the shape - */ - value: function updateBoundingBox() { - this.shape.updateBoundingBox(this.x, this.y); + key: 'drawArrows', + value: function drawArrows(ctx, viaNode) { + if (this.options.arrows.from.enabled === true) { + this.edgeType.drawArrowHead(ctx, 'from', viaNode, this.selected, this.hover); + } + if (this.options.arrows.middle.enabled === true) { + this.edgeType.drawArrowHead(ctx, 'middle', viaNode, this.selected, this.hover); + } + if (this.options.arrows.to.enabled === true) { + this.edgeType.drawArrowHead(ctx, 'to', viaNode, this.selected, this.hover); + } } }, { - key: 'resize', + key: 'drawLabel', + value: function drawLabel(ctx, viaNode) { + if (this.options.label !== undefined) { + // set style + var node1 = this.from; + var node2 = this.to; + var selected = this.from.selected || this.to.selected || this.selected; + if (node1.id != node2.id) { + var point = this.edgeType.getPoint(0.5, viaNode); + ctx.save(); - /** - * 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 - */ - value: function resize(ctx) { - this.shape.resize(ctx); + // if the label has to be rotated: + 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); + } + + // draw the label + this.labelModule.draw(ctx, point.x, point.y, selected); + ctx.restore(); + } else { + var x, y; + var radius = this.options.selfReferenceSize; + if (node1.shape.width > node1.shape.height) { + x = node1.x + node1.shape.width * 0.5; + y = node1.y - radius; + } else { + x = node1.x + radius; + y = node1.y - node1.shape.height * 0.5; + } + point = this._pointOnCircle(x, y, radius, 0.125); + this.labelModule.draw(ctx, point.x, point.y, 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 + * @param {Object} obj an object with parameters left, top + * @return {boolean} True if location is located on the edge */ 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; + if (this.connected) { + var distMax = 10; + var xFrom = this.from.x; + var yFrom = this.from.y; + var xTo = this.to.x; + var yTo = this.to.y; + var xObj = obj.left; + var yObj = obj.top; + + var dist = this.edgeType.getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); + + return dist < distMax; + } else { + return false; + } } }, { - key: 'isBoundingBoxOverlappingWith', + key: '_rotateForLabelAlignment', /** - * 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 + * Rotates the canvas so the text is most readable + * @param {CanvasRenderingContext2D} ctx + * @private */ - 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; + value: function _rotateForLabelAlignment(ctx) { + var dy = this.from.y - this.to.y; + var dx = this.from.x - this.to.x; + var angleInDegrees = Math.atan2(dy, dx); + + // rotate so label it is readable + if (angleInDegrees < -1 && dx < 0 || angleInDegrees > 0 && dx < 0) { + angleInDegrees = angleInDegrees + Math.PI; + } + + ctx.rotate(angleInDegrees); } - }], [{ - key: 'parseOptions', + }, { + key: '_pointOnCircle', /** - * 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 + * Get a point on a circle + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @param {Number} percentage. Value between 0 (line start) and 1 (line end) + * @return {Object} point + * @private */ + value: function _pointOnCircle(x, y, radius, percentage) { + var angle = percentage * 2 * Math.PI; + return { + x: x + radius * Math.cos(angle), + y: y - radius * Math.sin(angle) + }; + } + }, { + key: 'select', + value: function select() { + this.selected = true; + } + }, { + key: 'unselect', + value: function unselect() { + this.selected = false; + } + }], [{ + key: 'parseOptions', value: function parseOptions(parentOptions, newOptions) { var allowDeletion = arguments[2] === undefined ? false : arguments[2]; - var fields = ['color', 'font', 'fixed', 'shadow']; - util.selectiveNotDeepExtend(fields, parentOptions, newOptions, allowDeletion); + var fields = ['id', 'from', 'hidden', 'hoverWidth', 'label', 'length', 'line', 'opacity', 'physics', 'selectionWidth', 'selfReferenceSize', 'to', 'title', 'value', 'width']; - // merge the shadow options into the parent. + // only deep extend the items in the field array. These do not have shorthand. + util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion); + + util.mergeOptions(parentOptions, newOptions, 'smooth'); util.mergeOptions(parentOptions, newOptions, 'shadow'); - // individual shape newOptions - if (newOptions.color !== undefined && newOptions.color !== null) { - var parsedColor = util.parseColor(newOptions.color); - util.fillIfDefined(parentOptions.color, parsedColor); - } else if (allowDeletion === true && newOptions.color === null) { - parentOptions.color = undefined; - delete parentOptions.color; + if (newOptions.dashes !== undefined && newOptions.dashes !== null) { + parentOptions.dashes = newOptions.dashes; + } else if (allowDeletion === true && newOptions.dashes === null) { + parentOptions.dashes = undefined; + delete parentOptions.dashes; } - // 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; + // set the scaling newOptions + if (newOptions.scaling !== undefined && newOptions.scaling !== null) { + if (newOptions.scaling.min !== undefined) { + parentOptions.scaling.min = newOptions.scaling.min; + } + if (newOptions.scaling.max !== undefined) { + parentOptions.scaling.max = newOptions.scaling.max; + } + util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label'); + } else if (allowDeletion === true && newOptions.scaling === null) { + parentOptions.scaling = undefined; + delete parentOptions.scaling; + } + + // hanlde multiple input cases for arrows + if (newOptions.arrows !== undefined && newOptions.arrows !== null) { + if (typeof newOptions.arrows === 'string') { + var arrows = newOptions.arrows.toLowerCase(); + if (arrows.indexOf('to') != -1) { + parentOptions.arrows.to.enabled = true; + } + if (arrows.indexOf('middle') != -1) { + parentOptions.arrows.middle.enabled = true; + } + if (arrows.indexOf('from') != -1) { + parentOptions.arrows.from.enabled = true; + } + } else if (typeof newOptions.arrows === 'object') { + util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'to'); + util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'middle'); + util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'from'); } else { - if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') { - parentOptions.fixed.x = newOptions.fixed.x; + 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 = undefined; + delete parentOptions.arrows; + } + + // hanlde multiple input cases for color + if (newOptions.color !== undefined && newOptions.color !== null) { + if (util.isString(newOptions.color)) { + parentOptions.color.color = newOptions.color; + parentOptions.color.highlight = newOptions.color; + parentOptions.color.hover = newOptions.color; + parentOptions.color.inherit = false; + } else { + var colorsDefined = false; + if (newOptions.color.color !== undefined) { + parentOptions.color.color = newOptions.color.color;colorsDefined = true; } - if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') { - parentOptions.fixed.y = newOptions.fixed.y; + if (newOptions.color.highlight !== undefined) { + parentOptions.color.highlight = newOptions.color.highlight;colorsDefined = true; + } + if (newOptions.color.hover !== undefined) { + parentOptions.color.hover = newOptions.color.hover;colorsDefined = true; + } + if (newOptions.color.inherit !== undefined) { + parentOptions.color.inherit = newOptions.color.inherit; + } + if (newOptions.color.opacity !== undefined) { + parentOptions.color.opacity = Math.min(1, Math.max(0, newOptions.color.opacity)); + } + + if (newOptions.color.inherit === undefined && colorsDefined === true) { + parentOptions.color.inherit = false; } } + } else if (allowDeletion === true && newOptions.color === null) { + parentOptions.color = undefined; + delete parentOptions.color; } - // handle the font options + // handle the font settings if (newOptions.font !== undefined) { _sharedLabel2['default'].parseOptions(parentOptions.font, newOptions); } - - // handle the scaling options, specifically the label part - if (newOptions.scaling !== undefined) { - util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label'); - } } }]); - return Node; + return Edge; })(); - exports['default'] = Node; + exports['default'] = Edge; module.exports = exports['default']; /***/ }, -/* 75 */ +/* 76 */ /***/ function(module, exports, __webpack_require__) { - 'use strict'; + "use strict"; - Object.defineProperty(exports, '__esModule', { + 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 _slicedToArray(arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + 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 util = __webpack_require__(1); + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var Label = (function () { - function Label(body, options) { - _classCallCheck(this, Label); + var BarnesHutSolver = (function () { + function BarnesHutSolver(body, physicsBody, options) { + _classCallCheck(this, BarnesHutSolver); this.body = body; - - this.baseSize = undefined; + this.physicsBody = physicsBody; + this.barnesHutTree; this.setOptions(options); - this.size = { top: 0, left: 0, width: 0, height: 0, yLine: 0 }; // could be cached } - _createClass(Label, [{ - key: 'setOptions', + _createClass(BarnesHutSolver, [{ + key: "setOptions", value: function setOptions(options) { - var allowDeletion = arguments[1] === undefined ? false : arguments[1]; - this.options = options; - - if (options.label !== undefined) { - this.labelDirty = true; - } - - if (options.font !== undefined) { - Label.parseOptions(this.options.font, options, allowDeletion); - if (typeof options.font === 'string') { - this.baseSize = this.options.font.size; - } else if (typeof options.font === 'object') { - if (options.font.size !== undefined) { - this.baseSize = options.font.size; - } - } - } + 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: 'draw', + key: "solve", /** - * Main function. This is called from anything that wants to draw a label. - * @param ctx - * @param x - * @param y - * @param selected - * @param baseline + * 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 */ - value: function draw(ctx, x, y, selected) { - var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; - - // if no label, return - if (this.options.label === undefined) return; + 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; - // check if we have to render the label - var viewFontSize = this.options.font.size * this.body.view.scale; - if (this.options.label && viewFontSize < this.options.scaling.label.drawThreshold - 1) return; + // create the tree + var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices); - // update the size cache if required - this.calculateLabelSize(ctx, selected, x, y, baseline); + // for debugging + this.barnesHutTree = barnesHutTree; - // create the fontfill background - this._drawBackground(ctx); - // draw text - this._drawText(ctx, selected, x, y, baseline); + // 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); + } + } + } } }, { - key: '_drawBackground', + key: "_getForceContribution", /** - * Draws the label background - * @param {CanvasRenderingContext2D} ctx + * 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 */ - value: function _drawBackground(ctx) { - if (this.options.font.background !== undefined && this.options.font.background !== 'none') { - ctx.fillStyle = this.options.font.background; + 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; - var lineMargin = 2; + // 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); - switch (this.options.font.align) { - case 'middle': - ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); - break; - case 'top': - ctx.fillRect(-this.size.width * 0.5, -(this.size.height + lineMargin), this.size.width, this.size.height); - break; - case 'bottom': - ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height); - break; - default: - ctx.fillRect(this.size.left, this.size.top - 0.5 * lineMargin, this.size.width, this.size.height); - break; + // 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); + } + } } } } }, { - key: '_drawText', + key: "_calculateForces", /** + * Calculate the forces based on the distance. * - * @param ctx - * @param x - * @param baseline + * @param distance + * @param dx + * @param dy + * @param node + * @param parentBranch * @private */ - value: function _drawText(ctx, selected, x, y) { - var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; - - var fontSize = this.options.font.size; - var viewFontSize = fontSize * this.body.view.scale; - // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel) - if (viewFontSize >= this.options.scaling.label.maxVisible) { - fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale; + value: function _calculateForces(distance, dx, dy, node, parentBranch) { + if (distance === 0) { + distance = 0.1 * Math.random(); + dx = distance; } - var yLine = this.size.yLine; - - var _getColor = this._getColor(viewFontSize); + if (this.overlapAvoidanceFactor < 1) { + distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); + } - var _getColor2 = _slicedToArray(_getColor, 2); + // 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; - var fontColor = _getColor2[0]; - var strokeColor = _getColor2[1]; + this.physicsBody.forces[node.id].x += fx; + this.physicsBody.forces[node.id].y += fy; + } + }, { + key: "_formBarnesHutTree", - var _setAlignment = this._setAlignment(ctx, x, yLine, baseline); + /** + * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * + * @param nodes + * @param nodeIndices + * @private + */ + value: function _formBarnesHutTree(nodes, nodeIndices) { + var node = undefined; + var nodeCount = nodeIndices.length; - var _setAlignment2 = _slicedToArray(_setAlignment, 2); + var minX = nodes[nodeIndices[0]].x; + var minY = nodes[nodeIndices[0]].y; + var maxX = nodes[nodeIndices[0]].x; + var maxY = nodes[nodeIndices[0]].y; - x = _setAlignment2[0]; - yLine = _setAlignment2[1]; + // 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 - // configure context for drawing the text - ctx.font = (selected ? 'bold ' : '') + fontSize + 'px ' + this.options.font.face; - ctx.fillStyle = fontColor; - ctx.textAlign = 'center'; + var minimumTreeSize = 0.00001; + 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); - // set the strokeWidth - if (this.options.font.strokeWidth > 0) { - ctx.lineWidth = this.options.font.strokeWidth; - ctx.strokeStyle = strokeColor; - ctx.lineJoin = 'round'; - } + // 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); - // draw the text - for (var i = 0; i < this.lineCount; i++) { - if (this.options.font.strokeWidth > 0) { - ctx.strokeText(this.lines[i], x, yLine); + // 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); } - ctx.fillText(this.lines[i], x, yLine); - yLine += fontSize; } + + // make global + return barnesHutTree; } }, { - key: '_setAlignment', - value: function _setAlignment(ctx, x, yLine, baseline) { - // check for label alignment (for edges) - // TODO: make alignment for nodes - if (this.options.font.align !== 'horizontal') { - x = 0; - yLine = 0; + key: "_updateBranchMass", - var lineMargin = 2; - if (this.options.font.align === 'top') { - ctx.textBaseline = 'alphabetic'; - yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers - } else if (this.options.font.align === 'bottom') { - ctx.textBaseline = 'hanging'; - yLine += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers + /** + * this updates the mass of a branch. this is increased by adding a node. + * + * @param parentBranch + * @param node + * @private + */ + 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; + } + }, { + key: "_placeInTree", + + /** + * determine in which branch the node will be placed. + * + * @param parentBranch + * @param node + * @param skipMassUpdate + * @private + */ + 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 { - ctx.textBaseline = 'middle'; + // in SW + this._placeInRegion(parentBranch, node, "SW"); } } else { - ctx.textBaseline = baseline; + // 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"); + } } - - return [x, yLine]; } }, { - key: '_getColor', + key: "_placeInRegion", /** - * fade in when relative scale is between threshold and threshold - 1. - * If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here. + * actually place the node in a region (or branch) * - * @param viewFontSize - * @returns {*[]} + * @param parentBranch + * @param node + * @param region * @private */ - value: function _getColor(viewFontSize) { - var fontColor = this.options.font.color || '#000000'; - var strokeColor = this.options.font.strokeColor || '#ffffff'; - if (viewFontSize <= this.options.scaling.label.drawThreshold) { - var opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - viewFontSize))); - fontColor = util.overrideOpacity(fontColor, opacity); - strokeColor = util.overrideOpacity(strokeColor, opacity); + 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 += Math.random(); + node.y += Math.random(); + } 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; } - return [fontColor, strokeColor]; } }, { - key: 'getTextSize', + key: "_splitBranch", /** + * 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 ctx - * @param selected - * @returns {{width: number, height: number}} + * @param parentBranch + * @private */ - value: function getTextSize(ctx) { - var selected = arguments[1] === undefined ? false : arguments[1]; + 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"); - var size = { - width: this._processLabel(ctx, selected), - height: this.options.font.size * this.lineCount, - lineCount: this.lineCount - }; - return size; + if (containedNode != null) { + this._placeInTree(parentBranch, containedNode); + } } }, { - key: 'calculateLabelSize', + key: "_insertRegion", /** + * 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 ctx - * @param selected - * @param x - * @param y - * @param baseline + * @param parentBranch + * @param region + * @param parentRange + * @private */ - value: function calculateLabelSize(ctx, selected) { - var x = arguments[2] === undefined ? 0 : arguments[2]; - var y = arguments[3] === undefined ? 0 : arguments[3]; - var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; - - if (this.labelDirty === true) { - this.size.width = this._processLabel(ctx, selected); - } - this.size.height = this.options.font.size * this.lineCount; - 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.options.font.size; - if (baseline === 'hanging') { - this.size.top += 0.5 * this.options.font.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 + 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; } - this.labelDirty = false; + 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 + }; } }, { - key: '_processLabel', + key: "_debug", + + //--------------------------- DEBUGGING BELOW ---------------------------// /** - * This calculates the width as well as explodes the label string and calculates the amount of lines. + * This function is for debugging purposed, it draws the tree. + * * @param ctx - * @param selected - * @returns {number} + * @param color * @private */ - value: function _processLabel(ctx, selected) { - var width = 0; - var lines = ['']; - var lineCount = 0; - if (this.options.label !== undefined) { - lines = String(this.options.label).split('\n'); - lineCount = lines.length; - ctx.font = (selected ? 'bold ' : '') + this.options.font.size + 'px ' + this.options.font.face; - width = ctx.measureText(lines[0]).width; - for (var i = 1; i < lineCount; i++) { - var lineWidth = ctx.measureText(lines[i]).width; - width = lineWidth > width ? lineWidth : width; - } - } - this.lines = lines; - this.lineCount = lineCount; + value: function _debug(ctx, color) { + if (this.barnesHutTree !== undefined) { - return width; - } - }], [{ - key: 'parseOptions', - value: function parseOptions(parentOptions, newOptions) { - var allowDeletion = arguments[2] === undefined ? false : arguments[2]; + ctx.lineWidth = 1; - if (typeof newOptions.font === 'string') { - 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') { - util.fillIfDefined(parentOptions, newOptions.font, allowDeletion); + this._drawBranch(this.barnesHutTree.root, ctx, color); } - parentOptions.size = Number(parentOptions.size); } - }]); - - return Label; - })(); + }, { + key: "_drawBranch", - exports['default'] = Label; - module.exports = exports['default']; + /** + * This function is for debugging purposes. It draws the branches recursively. + * + * @param branch + * @param ctx + * @param color + * @private + */ + value: function _drawBranch(branch, ctx, color) { + if (color === undefined) { + color = "#FF0000"; + } -/***/ }, -/* 76 */ + 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"]; + +/***/ }, +/* 77 */ /***/ function(module, exports, __webpack_require__) { - 'use strict'; + "use strict"; - Object.defineProperty(exports, '__esModule', { + 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 _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"); } } - 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); - var _sharedLabel = __webpack_require__(75); + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } - var _sharedLabel2 = _interopRequireDefault(_sharedLabel); + _createClass(RepulsionSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + }, { + key: "solve", - var _edgesBezierEdgeDynamic = __webpack_require__(104); + /** + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + value: function solve() { + var dx, dy, distance, fx, fy, repulsingForce, node1, node2; - var _edgesBezierEdgeDynamic2 = _interopRequireDefault(_edgesBezierEdgeDynamic); + var nodes = this.body.nodes; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; - var _edgesBezierEdgeStatic = __webpack_require__(105); + // repulsing forces between nodes + var nodeDistance = this.options.nodeDistance; - var _edgesBezierEdgeStatic2 = _interopRequireDefault(_edgesBezierEdgeStatic); + // approximation constants + var a = -2 / 3 / nodeDistance; + var b = 4 / 3; - var _edgesStraightEdge = __webpack_require__(106); + // 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]]; - var _edgesStraightEdge2 = _interopRequireDefault(_edgesStraightEdge); + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); - var util = __webpack_require__(1); + // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping. + if (distance === 0) { + distance = 0.1 * Math.random(); + dx = distance; + } - /** - * @class Edge - * - * A edge connects two nodes - * @param {Object} properties Object with options. Must contain - * At least options from and to. - * Available options: from (number), - * to (number), label (string, color (string), - * width (number), style (string), - * length (number), title (string) - * @param {Network} network A Network object, used to find and edge to - * nodes. - * @param {Object} constants An object with default values for - * example for the color - */ + if (distance < 2 * nodeDistance) { + if (distance < 0.5 * nodeDistance) { + repulsingForce = 1; + } else { + repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / nodeDistance - 1) * steepness)) + } + repulsingForce = repulsingForce / distance; - var Edge = (function () { - function Edge(options, body, globalOptions) { - _classCallCheck(this, Edge); + fx = dx * repulsingForce; + fy = dy * repulsingForce; - if (body === undefined) { - throw 'No body provided'; + forces[node1.id].x -= fx; + forces[node1.id].y -= fy; + forces[node2.id].x += fx; + forces[node2.id].y += fy; + } + } + } } - this.options = util.bridgeObject(globalOptions); - this.body = body; + }]); - // initialize variables - this.id = undefined; - this.fromId = undefined; - this.toId = undefined; - this.selected = false; - this.hover = false; - this.labelDirty = true; - this.colorDirty = true; + return RepulsionSolver; + })(); - this.baseWidth = this.options.width; - this.baseFontSize = this.options.font.size; + exports["default"] = RepulsionSolver; + module.exports = exports["default"]; - this.from = undefined; // a node - this.to = undefined; // a node +/***/ }, +/* 78 */ +/***/ function(module, exports, __webpack_require__) { - this.edgeType = undefined; + "use strict"; - this.connected = false; + Object.defineProperty(exports, "__esModule", { + value: true + }); - this.labelModule = new _sharedLabel2['default'](this.body, this.options); + 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(Edge, [{ - key: 'setOptions', + _createClass(HierarchicalRepulsionSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + }, { + key: "solve", /** - * Set or overwrite options for the edge - * @param {Object} options an object with options - * @param doNotEmit + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private */ - value: function setOptions(options) { - if (!options) { - return; - } - this.colorDirty = true; + value: function solve() { + var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j; - Edge.parseOptions(this.options, options, true); + var nodes = this.body.nodes; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; - if (options.id !== undefined) { - this.id = options.id; - } - if (options.from !== undefined) { - this.fromId = options.from; - } - if (options.to !== undefined) { - this.toId = options.to; - } - if (options.title !== undefined) { - this.title = options.title; - } - if (options.value !== undefined) { - options.value = parseInt(options.value); + // 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; + } + } } + } + }]); - // A node is connected when it has a from and to node that both exist in the network.body.nodes. - this.connect(); + return HierarchicalRepulsionSolver; + })(); - // update label Module - this.updateLabelModule(); + exports["default"] = HierarchicalRepulsionSolver; + module.exports = exports["default"]; - var dataChanged = this.updateEdgeType(); +/***/ }, +/* 79 */ +/***/ function(module, exports, __webpack_require__) { - // if anything has been updates, reset the selection width and the hover width - this._setInteractionWidths(); + "use strict"; - return dataChanged; - } - }, { - key: 'updateLabelModule', + Object.defineProperty(exports, "__esModule", { + value: true + }); - /** - * update the options in the label module - */ - value: function updateLabelModule() { - this.labelModule.setOptions(this.options, true); - if (this.labelModule.baseSize !== undefined) { - this.baseFontSize = this.labelModule.baseSize; - } + 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; } }, { - key: 'updateEdgeType', + key: "solve", /** - * update the edge type, set the options - * @returns {boolean} + * This function calculates the springforces on the nodes, accounting for the support nodes. + * + * @private */ - value: function updateEdgeType() { - var dataChanged = false; - var changeInType = true; - if (this.edgeType !== undefined) { - if (this.edgeType instanceof _edgesBezierEdgeDynamic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type === 'dynamic') { - changeInType = false; - } - if (this.edgeType instanceof _edgesBezierEdgeStatic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type !== 'dynamic') { - changeInType = false; - } - if (this.edgeType instanceof _edgesStraightEdge2['default'] && this.options.smooth.enabled === false) { - changeInType = false; - } + value: function solve() { + var edgeLength = undefined, + edge = undefined; + var edgeIndices = this.physicsBody.physicsEdgeIndices; + var edges = this.body.edges; + var node1 = undefined, + node2 = undefined, + node3 = undefined; - if (changeInType === true) { - dataChanged = this.edgeType.cleanup(); - } - } + // 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 = edge.to; + node2 = edge.edgeType.via; + node3 = edge.from; - if (changeInType === true) { - if (this.options.smooth.enabled === true) { - if (this.options.smooth.type === 'dynamic') { - dataChanged = true; - this.edgeType = new _edgesBezierEdgeDynamic2['default'](this.options, this.body, this.labelModule); - } else { - this.edgeType = new _edgesBezierEdgeStatic2['default'](this.options, this.body, this.labelModule); + 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(edge.from, edge.to, edgeLength); + } } - } else { - this.edgeType = new _edgesStraightEdge2['default'](this.options, this.body, this.labelModule); } - } else { - // if nothing changes, we just set the options. - this.edgeType.setOptions(this.options); } - - return dataChanged; } }, { - key: 'togglePhysics', + key: "_calculateSpringForce", /** - * Enable or disable the physics. - * @param status + * This is the code actually performing the calculation for the function above. + * + * @param node1 + * @param node2 + * @param edgeLength + * @private */ - value: function togglePhysics(status) { - this.options.physics = status; - this.edgeType.togglePhysics(status); - } - }, { - key: 'connect', + 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); - /** - * Connect an edge to its nodes - */ - value: function connect() { - this.disconnect(); + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + var springForce = this.options.springConstant * (edgeLength - distance) / distance; - this.from = this.body.nodes[this.fromId] || undefined; - this.to = this.body.nodes[this.toId] || undefined; - this.connected = this.from !== undefined && this.to !== undefined; + var fx = dx * springForce; + var fy = dy * springForce; - if (this.connected === true) { - this.from.attachEdge(this); - this.to.attachEdge(this); - } else { - if (this.from) { - this.from.detachEdge(this); - } - if (this.to) { - this.to.detachEdge(this); - } + // 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; } - } - }, { - key: 'disconnect', - /** - * Disconnect an edge from its nodes - */ - value: function disconnect() { - if (this.from) { - this.from.detachEdge(this); - this.from = undefined; - } - if (this.to) { - this.to.detachEdge(this); - this.to = undefined; + if (this.physicsBody.forces[node2.id] !== undefined) { + this.physicsBody.forces[node2.id].x -= fx; + this.physicsBody.forces[node2.id].y -= fy; } - - 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. - */ - value: function getTitle() { - return this.title; - } - }, { - key: 'isSelected', + return SpringSolver; + })(); - /** - * check if this node is selecte - * @return {boolean} selected True if node is selected, else false - */ - value: function isSelected() { - return this.selected; - } - }, { - key: 'getValue', + exports["default"] = SpringSolver; + module.exports = exports["default"]; - /** - * Retrieve the value of the edge. Can be undefined - * @return {Number} value - */ - value: function getValue() { - return this.options.value; +/***/ }, +/* 80 */ +/***/ 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 _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; } }, { - key: 'setValueRange', + key: "solve", /** - * Adjust the value range of the edge. The edge will adjust it's width - * based on its value. - * @param {Number} min - * @param {Number} max - * @param total + * This function calculates the springforces on the nodes, accounting for the support nodes. + * + * @private */ - value: function setValueRange(min, max, total) { - if (this.options.value !== undefined) { - var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); - var widthDiff = this.options.scaling.max - this.options.scaling.min; - if (this.options.scaling.label.enabled === true) { - var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; - this.options.font.size = this.options.scaling.label.min + scale * fontDiff; - } - this.options.width = this.options.scaling.min + scale * widthDiff; - } else { - this.options.width = this.baseWidth; - this.options.font.size = this.baseFontSize; - } + value: function solve() { + var edgeLength, edge; + var dx, dy, fx, fy, springForce, distance; + var edges = this.body.edges; + var factor = 0.5; - this._setInteractionWidths(); - } - }, { - key: '_setInteractionWidths', - value: function _setInteractionWidths() { - if (typeof this.options.hoverWidth === 'function') { - this.edgeType.hoverWidth = this.options.hoverWidth(this.options.width); - } else { - this.edgeType.hoverWidth = this.options.hoverWidth + this.options.width; - } + var edgeIndices = this.physicsBody.physicsEdgeIndices; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; - if (typeof this.options.selectionWidth === 'function') { - this.edgeType.selectionWidth = this.options.selectionWidth(this.options.width); - } else { - this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width; + // 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; } - } - }, { - key: 'draw', - /** - * Redraw a edge - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - */ - value: function draw(ctx) { - var via = this.edgeType.drawLine(ctx, this.selected, this.hover); - this.drawArrows(ctx, via); - this.drawLabel(ctx, via); - } - }, { - key: 'drawArrows', - value: function drawArrows(ctx, viaNode) { - if (this.options.arrows.from.enabled === true) { - this.edgeType.drawArrowHead(ctx, 'from', viaNode, this.selected, this.hover); - } - if (this.options.arrows.middle.enabled === true) { - this.edgeType.drawArrowHead(ctx, 'middle', viaNode, this.selected, this.hover); - } - if (this.options.arrows.to.enabled === true) { - this.edgeType.drawArrowHead(ctx, 'to', viaNode, this.selected, this.hover); - } - } - }, { - key: 'drawLabel', - value: function drawLabel(ctx, viaNode) { - if (this.options.label !== undefined) { - // set style - var node1 = this.from; - var node2 = this.to; - var selected = this.from.selected || this.to.selected || this.selected; - if (node1.id != node2.id) { - var point = this.edgeType.getPoint(0.5, viaNode); - ctx.save(); + // 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; - // if the label has to be rotated: - 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); - } + 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; - // draw the label - this.labelModule.draw(ctx, point.x, point.y, selected); - ctx.restore(); - } else { - var x, y; - var radius = this.options.selfReferenceSize; - if (node1.shape.width > node1.shape.height) { - x = node1.x + node1.shape.width * 0.5; - y = node1.y - radius; + // 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) { + forces[edge.toId].springFx -= fx; + forces[edge.toId].springFy -= fy; + forces[edge.fromId].springFx += fx; + forces[edge.fromId].springFy += fy; } else { - x = node1.x + radius; - y = node1.y - node1.shape.height * 0.5; + forces[edge.toId].x -= factor * fx; + forces[edge.toId].y -= factor * fy; + forces[edge.fromId].x += factor * fx; + forces[edge.fromId].y += factor * fy; } - point = this._pointOnCircle(x, y, radius, 0.125); - this.labelModule.draw(ctx, point.x, point.y, selected); } } - } - }, { - 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 - */ - value: function isOverlappingWith(obj) { - if (this.connected) { - var distMax = 10; - var xFrom = this.from.x; - var yFrom = this.from.y; - var xTo = this.to.x; - var yTo = this.to.y; - var xObj = obj.left; - var yObj = obj.top; - - var dist = this.edgeType.getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); - - return dist < distMax; - } else { - return false; - } - } - }, { - key: '_rotateForLabelAlignment', - - /** - * Rotates the canvas so the text is most readable - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _rotateForLabelAlignment(ctx) { - var dy = this.from.y - this.to.y; - var dx = this.from.x - this.to.x; - var angleInDegrees = Math.atan2(dy, dx); - - // rotate so label it is readable - if (angleInDegrees < -1 && dx < 0 || angleInDegrees > 0 && dx < 0) { - angleInDegrees = angleInDegrees + Math.PI; - } - - ctx.rotate(angleInDegrees); - } - }, { - key: '_pointOnCircle', - - /** - * Get a point on a circle - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @param {Number} percentage. Value between 0 (line start) and 1 (line end) - * @return {Object} point - * @private - */ - value: function _pointOnCircle(x, y, radius, percentage) { - var angle = percentage * 2 * Math.PI; - return { - x: x + radius * Math.cos(angle), - y: y - radius * Math.sin(angle) - }; - } - }, { - key: 'select', - value: function select() { - this.selected = true; - } - }, { - key: 'unselect', - value: function unselect() { - this.selected = false; - } - }], [{ - key: 'parseOptions', - value: function parseOptions(parentOptions, newOptions) { - var allowDeletion = arguments[2] === undefined ? false : arguments[2]; - - var fields = ['id', 'from', 'hidden', 'hoverWidth', 'label', 'length', 'line', 'opacity', 'physics', 'selectionWidth', 'selfReferenceSize', 'to', 'title', 'value', 'width']; - - // only deep extend the items in the field array. These do not have shorthand. - util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion); - - util.mergeOptions(parentOptions, newOptions, 'smooth'); - util.mergeOptions(parentOptions, newOptions, 'shadow'); - - if (newOptions.dashes !== undefined && newOptions.dashes !== null) { - parentOptions.dashes = newOptions.dashes; - } else if (allowDeletion === true && newOptions.dashes === null) { - parentOptions.dashes = undefined; - delete parentOptions.dashes; - } - // set the scaling newOptions - if (newOptions.scaling !== undefined && newOptions.scaling !== null) { - if (newOptions.scaling.min !== undefined) { - parentOptions.scaling.min = newOptions.scaling.min; - } - if (newOptions.scaling.max !== undefined) { - parentOptions.scaling.max = newOptions.scaling.max; - } - util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label'); - } else if (allowDeletion === true && newOptions.scaling === null) { - parentOptions.scaling = undefined; - delete parentOptions.scaling; - } + // 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)); - // hanlde multiple input cases for arrows - if (newOptions.arrows !== undefined && newOptions.arrows !== null) { - if (typeof newOptions.arrows === 'string') { - var arrows = newOptions.arrows.toLowerCase(); - if (arrows.indexOf('to') != -1) { - parentOptions.arrows.to.enabled = true; - } - if (arrows.indexOf('middle') != -1) { - parentOptions.arrows.middle.enabled = true; - } - if (arrows.indexOf('from') != -1) { - parentOptions.arrows.from.enabled = true; - } - } else if (typeof newOptions.arrows === 'object') { - util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'to'); - util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'middle'); - util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'from'); - } 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)); - } - } else if (allowDeletion === true && newOptions.arrows === null) { - parentOptions.arrows = undefined; - delete parentOptions.arrows; + forces[nodeId].x += springFx; + forces[nodeId].y += springFy; } - // hanlde multiple input cases for color - if (newOptions.color !== undefined && newOptions.color !== null) { - if (util.isString(newOptions.color)) { - parentOptions.color.color = newOptions.color; - parentOptions.color.highlight = newOptions.color; - parentOptions.color.hover = newOptions.color; - parentOptions.color.inherit = false; - } else { - var colorsDefined = false; - if (newOptions.color.color !== undefined) { - parentOptions.color.color = newOptions.color.color;colorsDefined = true; - } - if (newOptions.color.highlight !== undefined) { - parentOptions.color.highlight = newOptions.color.highlight;colorsDefined = true; - } - if (newOptions.color.hover !== undefined) { - parentOptions.color.hover = newOptions.color.hover;colorsDefined = true; - } - if (newOptions.color.inherit !== undefined) { - parentOptions.color.inherit = newOptions.color.inherit; - } - if (newOptions.color.opacity !== undefined) { - parentOptions.color.opacity = Math.min(1, Math.max(0, newOptions.color.opacity)); - } - - if (newOptions.color.inherit === undefined && colorsDefined === true) { - parentOptions.color.inherit = false; - } - } - } else if (allowDeletion === true && newOptions.color === null) { - parentOptions.color = undefined; - delete parentOptions.color; + // 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; - // handle the font settings - if (newOptions.font !== undefined) { - _sharedLabel2['default'].parseOptions(parentOptions.font, newOptions); + for (var i = 0; i < nodeIndices.length; i++) { + var nodeId = nodeIndices[i]; + forces[nodeId].x -= correctionFx; + forces[nodeId].y -= correctionFy; } } }]); - return Edge; + return HierarchicalSpringSolver; })(); - exports['default'] = Edge; - module.exports = exports['default']; + exports["default"] = HierarchicalSpringSolver; + module.exports = exports["default"]; /***/ }, -/* 77 */ +/* 81 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -36062,104 +36521,97 @@ return /******/ (function(modules) { // webpackBootstrap 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); + var CentralGravitySolver = (function () { + function CentralGravitySolver(body, physicsBody, options) { + _classCallCheck(this, CentralGravitySolver); this.body = body; this.physicsBody = physicsBody; - this.barnesHutTree; this.setOptions(options); } - _createClass(BarnesHutSolver, [{ + _createClass(CentralGravitySolver, [{ 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: "solve", - - /** - * 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 - */ 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); + 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 debugging - this.barnesHutTree = barnesHutTree; + 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); - // 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._calculateForces(distance, dx, dy, forces, node); } } }, { - key: "_getForceContribution", + key: "_calculateForces", /** - * 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 + * Calculate the forces based on the distance. * @private */ - 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; + 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; + } + }]); - // 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); + return CentralGravitySolver; + })(); - // 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); - } - } - } - } - } - }, { + exports["default"] = CentralGravitySolver; + module.exports = exports["default"]; + +/***/ }, +/* 82 */ +/***/ 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) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; 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) subClass.__proto__ = superClass; } + + var _BarnesHutSolver2 = __webpack_require__(76); + + var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); + + var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) { + function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); + + _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); + } + + _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); + + _createClass(ForceAtlas2BasedRepulsionSolver, [{ key: "_calculateForces", /** @@ -36182,986 +36634,763 @@ return /******/ (function(modules) { // webpackBootstrap 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 / Math.pow(distance, 3); + 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; } - }, { - key: "_formBarnesHutTree", + }]); - /** - * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. - * - * @param nodes - * @param nodeIndices - * @private - */ - value: function _formBarnesHutTree(nodes, nodeIndices) { - var node = undefined; - var nodeCount = nodeIndices.length; + return ForceAtlas2BasedRepulsionSolver; + })(_BarnesHutSolver3["default"]); - var minX = nodes[nodeIndices[0]].x; - var minY = nodes[nodeIndices[0]].y; - var maxX = nodes[nodeIndices[0]].x; - var maxY = nodes[nodeIndices[0]].y; + exports["default"] = ForceAtlas2BasedRepulsionSolver; + module.exports = exports["default"]; - // 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 +/***/ }, +/* 83 */ +/***/ function(module, exports, __webpack_require__) { - var minimumTreeSize = 0.00001; - 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); + "use strict"; - // 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; - } - }, { - key: "_updateBranchMass", - - /** - * this updates the mass of a branch. this is increased by adding a node. - * - * @param parentBranch - * @param node - * @private - */ - 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; - } - }, { - key: "_placeInTree", + Object.defineProperty(exports, "__esModule", { + value: true + }); - /** - * determine in which branch the node will be placed. - * - * @param parentBranch - * @param node - * @param skipMassUpdate - * @private - */ - value: function _placeInTree(parentBranch, node, skipMassUpdate) { - if (skipMassUpdate != true || skipMassUpdate === undefined) { - // update the mass of the branch. - this._updateBranchMass(parentBranch, node); - } + 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; }; })(); - 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"); - } - } - } - }, { - key: "_placeInRegion", + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; 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); } } }; - /** - * actually place the node in a region (or branch) - * - * @param parentBranch - * @param node - * @param region - * @private - */ - 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 += Math.random(); - node.y += Math.random(); - } 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; - } - } - }, { - key: "_splitBranch", + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /** - * 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 - */ - 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"); + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - if (containedNode != null) { - this._placeInTree(parentBranch, containedNode); - } - } - }, { - key: "_insertRegion", + 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; } - /** - * 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 - */ - 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; - } + var _CentralGravitySolver2 = __webpack_require__(81); - 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 - }; - } - }, { - key: "_debug", + var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2); - //--------------------------- DEBUGGING BELOW ---------------------------// + var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) { + function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { + _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver); - /** - * This function is for debugging purposed, it draws the tree. - * - * @param ctx - * @param color - * @private - */ - value: function _debug(ctx, color) { - if (this.barnesHutTree !== undefined) { + _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); + } - ctx.lineWidth = 1; + _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); - this._drawBranch(this.barnesHutTree.root, ctx, color); - } - } - }, { - key: "_drawBranch", + _createClass(ForceAtlas2BasedCentralGravitySolver, [{ + key: "_calculateForces", /** - * This function is for debugging purposes. It draws the branches recursively. - * - * @param branch - * @param ctx - * @param color + * Calculate the forces based on the distance. * @private */ - 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); + 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; } - 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; - })(); + return ForceAtlas2BasedCentralGravitySolver; + })(_CentralGravitySolver3["default"]); - exports["default"] = BarnesHutSolver; + exports["default"] = ForceAtlas2BasedCentralGravitySolver; module.exports = exports["default"]; /***/ }, -/* 78 */ +/* 84 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; + 'use strict'; - Object.defineProperty(exports, "__esModule", { + 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; - } - }, { - key: "solve", + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; 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); } } }; - /** - * Calculate the forces the nodes apply on each other based on a repulsion field. - * This field is linearly approximated. - * - * @private - */ - value: function solve() { - var dx, dy, distance, fx, fy, repulsingForce, node1, node2; + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - var nodes = this.body.nodes; - var nodeIndices = this.physicsBody.physicsNodeIndices; - var forces = this.physicsBody.forces; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - // repulsing forces between nodes - var nodeDistance = this.options.nodeDistance; + 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; } - // approximation constants - var a = -2 / 3 / nodeDistance; - var b = 4 / 3; + var _Node2 = __webpack_require__(73); - // 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]]; + var _Node3 = _interopRequireDefault(_Node2); - 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; - } + var Cluster = (function (_Node) { + function Cluster(options, body, imagelist, grouplist, globalOptions) { + _classCallCheck(this, Cluster); - if (distance < 2 * nodeDistance) { - if (distance < 0.5 * nodeDistance) { - repulsingForce = 1; - } else { - repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / nodeDistance - 1) * steepness)) - } - repulsingForce = repulsingForce / distance; + _get(Object.getPrototypeOf(Cluster.prototype), 'constructor', this).call(this, options, body, imagelist, grouplist, globalOptions); - fx = dx * repulsingForce; - fy = dy * repulsingForce; + this.isCluster = true; + this.containedNodes = {}; + this.containedEdges = {}; + } - forces[node1.id].x -= fx; - forces[node1.id].y -= fy; - forces[node2.id].x += fx; - forces[node2.id].y += fy; - } - } - } - } - }]); + _inherits(Cluster, _Node); - return RepulsionSolver; - })(); + return Cluster; + })(_Node3['default']); - exports["default"] = RepulsionSolver; - module.exports = exports["default"]; + exports['default'] = Cluster; + module.exports = exports['default']; /***/ }, -/* 79 */ +/* 85 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; + 'use strict'; - Object.defineProperty(exports, "__esModule", { + 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 _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"); } } + 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); + var util = __webpack_require__(1); + var Hammer = __webpack_require__(41); + var hammerUtil = __webpack_require__(49); + var keycharm = __webpack_require__(87); + + var NavigationHandler = (function () { + function NavigationHandler(body, canvas) { + var _this = this; + + _classCallCheck(this, NavigationHandler); this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); + this.canvas = canvas; + + this.iconsCreated = false; + this.navigationHammers = []; + this.boundFunctions = {}; + this.touchTime = 0; + this.activated = false; + + this.body.emitter.on('release', function () { + _this._stopMovement(); + }); + this.body.emitter.on('activate', function () { + _this.activated = true;_this.configureKeyboardBindings(); + }); + this.body.emitter.on('deactivate', function () { + _this.activated = false;_this.configureKeyboardBindings(); + }); + this.body.emitter.on('destroy', function () { + if (_this.keycharm !== undefined) { + _this.keycharm.destroy(); + } + }); + + this.options = {}; } - _createClass(HierarchicalRepulsionSolver, [{ - key: "setOptions", + _createClass(NavigationHandler, [{ + key: 'setOptions', value: function setOptions(options) { - this.options = options; + if (options !== undefined) { + this.options = options; + this.create(); + } } }, { - key: "solve", + key: 'create', + value: function create() { + if (this.options.navigationButtons === true) { + if (this.iconsCreated === false) { + this.loadNavigationElements(); + } + } else if (this.iconsCreated === true) { + this.cleanNavigation(); + } + + this.configureKeyboardBindings(); + } + }, { + key: 'cleanNavigation', + value: function cleanNavigation() { + // clean hammer bindings + if (this.navigationHammers.length != 0) { + for (var i = 0; i < this.navigationHammers.length; i++) { + this.navigationHammers[i].destroy(); + } + this.navigationHammers = []; + } + + this._navigationReleaseOverload = function () {}; + + // clean up previous navigation items + if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) { + this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']); + } + + this.iconsCreated = false; + } + }, { + key: 'loadNavigationElements', /** - * Calculate the forces the nodes apply on each other based on a repulsion field. - * This field is linearly approximated. + * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation + * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent + * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. + * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. * * @private */ - value: function solve() { - var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j; + value: function loadNavigationElements() { + this.cleanNavigation(); - var nodes = this.body.nodes; - var nodeIndices = this.physicsBody.physicsNodeIndices; - var forces = this.physicsBody.forces; + this.navigationDOM = {}; + var navigationDivs = ['up', 'down', 'left', 'right', 'zoomIn', 'zoomOut', 'zoomExtends']; + var navigationDivActions = ['_moveUp', '_moveDown', '_moveLeft', '_moveRight', '_zoomIn', '_zoomOut', '_fit']; - // repulsing forces between nodes - var nodeDistance = this.options.nodeDistance; + this.navigationDOM['wrapper'] = document.createElement('div'); + this.navigationDOM['wrapper'].className = 'vis-navigation'; + this.canvas.frame.appendChild(this.navigationDOM['wrapper']); - // 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]]; + for (var i = 0; i < navigationDivs.length; i++) { + this.navigationDOM[navigationDivs[i]] = document.createElement('div'); + this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i]; + this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]); - // 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 hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); + if (navigationDivActions[i] === '_fit') { + hammerUtil.onTouch(hammer, this._fit.bind(this)); + } else { + hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i])); + } - 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; + this.navigationHammers.push(hammer); + } - forces[node1.id].x -= fx; - forces[node1.id].y -= fy; - forces[node2.id].x += fx; - forces[node2.id].y += fy; - } - } + this.iconsCreated = true; + } + }, { + key: 'bindToRedraw', + 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'); } } - }]); - - return HierarchicalRepulsionSolver; - })(); - - exports["default"] = HierarchicalRepulsionSolver; - module.exports = exports["default"]; - -/***/ }, -/* 80 */ -/***/ 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 _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; + }, { + key: 'unbindFromRedraw', + value: function unbindFromRedraw(action) { + if (this.boundFunctions[action] !== undefined) { + this.body.emitter.off('initRedraw', this.boundFunctions[action]); + this.body.emitter.emit('_stopRendering'); + delete this.boundFunctions[action]; + } } }, { - key: "solve", + key: '_fit', /** - * This function calculates the springforces on the nodes, accounting for the support nodes. + * this stops all movement induced by the navigation buttons * * @private */ - value: function solve() { - var edgeLength = undefined, - edge = undefined; - var edgeIndices = this.physicsBody.physicsEdgeIndices; - var edges = this.body.edges; - 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 = edge.to; - node2 = edge.edgeType.via; - node3 = edge.from; - - 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(edge.from, edge.to, edgeLength); - } - } - } + 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.touchTime = new Date().valueOf(); } } }, { - key: "_calculateSpringForce", + key: '_stopMovement', /** - * This is the code actually performing the calculation for the function above. + * this stops all movement induced by the navigation buttons * - * @param node1 - * @param node2 - * @param edgeLength * @private */ - 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; + 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.boundFunctions = {}; } - }]); - - return SpringSolver; - })(); - - exports["default"] = SpringSolver; - module.exports = exports["default"]; - -/***/ }, -/* 81 */ -/***/ 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 _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; + }, { + key: '_moveUp', + value: function _moveUp() { + this.body.view.translation.y += this.options.keyboard.speed.y; } }, { - key: "solve", + key: '_moveDown', + value: function _moveDown() { + this.body.view.translation.y -= this.options.keyboard.speed.y; + } + }, { + key: '_moveLeft', + value: function _moveLeft() { + this.body.view.translation.x += this.options.keyboard.speed.x; + } + }, { + key: '_moveRight', + value: function _moveRight() { + this.body.view.translation.x -= this.options.keyboard.speed.x; + } + }, { + key: '_zoomIn', + value: function _zoomIn() { + this.body.view.scale *= 1 + this.options.keyboard.speed.zoom; + } + }, { + key: '_zoomOut', + value: function _zoomOut() { + this.body.view.scale /= 1 + this.options.keyboard.speed.zoom; + } + }, { + key: 'configureKeyboardBindings', /** - * This function calculates the springforces on the nodes, accounting for the support nodes. - * - * @private + * bind all keys using keycharm. */ - 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; + value: function configureKeyboardBindings() { + if (this.keycharm !== undefined) { + this.keycharm.destroy(); } - // 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 (this.options.keyboard.enabled === true) { - if (edge.to.level != edge.from.level) { - forces[edge.toId].springFx -= fx; - forces[edge.toId].springFy -= fy; - forces[edge.fromId].springFx += fx; - forces[edge.fromId].springFy += fy; - } else { - forces[edge.toId].x -= factor * fx; - forces[edge.toId].y -= factor * fy; - forces[edge.fromId].x += factor * fx; - forces[edge.fromId].y += factor * fy; - } + if (this.options.keyboard.bindToWindow === true) { + this.keycharm = keycharm({ container: window, preventDefault: true }); + } else { + this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true }); } - } - - // 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; - } + this.keycharm.reset(); - // 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; + if (this.activated === true) { + this.keycharm.bind('up', this.bindToRedraw.bind(this, '_moveUp'), 'keydown'); + this.keycharm.bind('down', this.bindToRedraw.bind(this, '_moveDown'), 'keydown'); + this.keycharm.bind('left', this.bindToRedraw.bind(this, '_moveLeft'), 'keydown'); + this.keycharm.bind('right', this.bindToRedraw.bind(this, '_moveRight'), 'keydown'); + this.keycharm.bind('=', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('num+', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('num-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + this.keycharm.bind('-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + this.keycharm.bind('[', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + this.keycharm.bind(']', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('pageup', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('pagedown', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - for (var i = 0; i < nodeIndices.length; i++) { - var nodeId = nodeIndices[i]; - forces[nodeId].x -= correctionFx; - forces[nodeId].y -= correctionFy; + this.keycharm.bind('up', this.unbindFromRedraw.bind(this, '_moveUp'), 'keyup'); + this.keycharm.bind('down', this.unbindFromRedraw.bind(this, '_moveDown'), 'keyup'); + this.keycharm.bind('left', this.unbindFromRedraw.bind(this, '_moveLeft'), 'keyup'); + this.keycharm.bind('right', this.unbindFromRedraw.bind(this, '_moveRight'), 'keyup'); + this.keycharm.bind('=', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('num+', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('num-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + this.keycharm.bind('-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + this.keycharm.bind('[', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + this.keycharm.bind(']', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('pageup', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('pagedown', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + } } } }]); - return HierarchicalSpringSolver; + return NavigationHandler; })(); - exports["default"] = HierarchicalSpringSolver; - module.exports = exports["default"]; + exports['default'] = NavigationHandler; + module.exports = exports['default']; /***/ }, -/* 82 */ +/* 86 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; + 'use strict'; - Object.defineProperty(exports, "__esModule", { + 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 _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"); } } + 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); + /** + * Popup is a class to create a popup window with some text + * @param {Element} container The container object. + * @param {Number} [x] + * @param {Number} [y] + * @param {String} [text] + * @param {Object} [style] An object containing borderColor, + * backgroundColor, etc. + */ - this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); + var Popup = (function () { + function Popup(container) { + _classCallCheck(this, Popup); + + this.container = container; + + this.x = 0; + this.y = 0; + this.padding = 5; + this.hidden = false; + + // create the frame + this.frame = document.createElement('div'); + this.frame.className = 'vis-network-tooltip'; + this.container.appendChild(this.frame); } - _createClass(CentralGravitySolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; + _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: "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; + key: 'setText', - 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); + /** + * Set the content for the popup window. This can be HTML code or text. + * @param {string | Element} content + */ + value: function setText(content) { + if (content instanceof Element) { + this.frame.innerHTML = ''; + this.frame.appendChild(content); + } else { + this.frame.innerHTML = content; // string containing text or HTML + } + } + }, { + key: 'show', - this._calculateForces(distance, dx, dy, forces, node); + /** + * Show the popup window + * @param {boolean} [doShow] Show or hide the window + */ + value: function show(doShow) { + if (doShow === undefined) { + doShow = true; + } + + if (doShow === true) { + var height = this.frame.clientHeight; + var width = this.frame.clientWidth; + var maxHeight = this.frame.parentNode.clientHeight; + var maxWidth = this.frame.parentNode.clientWidth; + + var top = this.y - height; + if (top + height + this.padding > maxHeight) { + top = maxHeight - height - this.padding; + } + if (top < this.padding) { + top = this.padding; + } + + var left = this.x; + if (left + width + this.padding > maxWidth) { + left = maxWidth - width - this.padding; + } + if (left < this.padding) { + left = this.padding; + } + + this.frame.style.left = left + 'px'; + this.frame.style.top = top + 'px'; + this.frame.style.visibility = 'visible'; + this.hidden = false; + } else { + this.hide(); } } }, { - key: "_calculateForces", + key: 'hide', /** - * Calculate the forces based on the distance. - * @private + * Hide the popup window */ - 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; + value: function hide() { + this.hidden = true; + this.frame.style.visibility = 'hidden'; } }]); - return CentralGravitySolver; + return Popup; })(); - exports["default"] = CentralGravitySolver; - module.exports = exports["default"]; + exports['default'] = Popup; + module.exports = exports['default']; /***/ }, -/* 83 */ +/* 87 */ /***/ 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) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; 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) subClass.__proto__ = superClass; } - - var _BarnesHutSolver2 = __webpack_require__(77); + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; + /** + * Created by Alex on 11/6/2014. + */ - var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); + // 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 () { - var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) { - function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { - _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); + function keycharm(options) { + var preventDefault = options && options.preventDefault || false; - _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); - } + var container = options && options.container || window; + var _exportFunctions = {}; + var _bound = {keydown:{}, keyup:{}}; + var _keys = {}; + var i; - _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); + // 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};} - _createClass(ForceAtlas2BasedRepulsionSolver, [{ - key: "_calculateForces", + // 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}; - /** - * 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(); - dx = distance; + + + 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(); + } } + }; - if (this.overlapAvoidanceFactor < 1) { - distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); + // 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}); + }; - 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; - } - }]); + // 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); + } + } + }; - return ForceAtlas2BasedRepulsionSolver; - })(_BarnesHutSolver3["default"]); + // 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"; + }; - exports["default"] = ForceAtlas2BasedRepulsionSolver; - module.exports = exports["default"]; + // 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] = []; + } + }; -/***/ }, -/* 84 */ -/***/ function(module, exports, __webpack_require__) { + // reset all bound variables. + _exportFunctions.reset = function() { + _bound = {keydown:{}, keyup:{}}; + }; - "use strict"; + // unbind all listeners and reset all variables. + _exportFunctions.destroy = function() { + _bound = {keydown:{}, keyup:{}}; + container.removeEventListener('keydown', down, true); + container.removeEventListener('keyup', up, true); + }; - Object.defineProperty(exports, "__esModule", { - value: true - }); + // create listeners. + container.addEventListener('keydown',down,true); + container.addEventListener('keyup',up,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; }; })(); + // return the public functions. + return _exportFunctions; + } - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; 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); } } }; + return keycharm; + })); - 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) subClass.__proto__ = superClass; } - var _CentralGravitySolver2 = __webpack_require__(82); +/***/ }, +/* 88 */ +/***/ function(module, exports, __webpack_require__) { - var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2); + function webpackContext(req) { + throw new Error("Cannot find module '" + req + "'."); + } + webpackContext.keys = function() { return []; }; + webpackContext.resolve = webpackContext; + module.exports = webpackContext; + webpackContext.id = 88; - var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) { - function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { - _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver); - _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); - } +/***/ }, +/* 89 */ +/***/ function(module, exports, __webpack_require__) { - _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; + } - _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; - var gravityForce = this.options.centralGravity * degree * node.options.mass; - forces[node.id].x = dx * gravityForce; - forces[node.id].y = dy * gravityForce; - } - } - }]); +/***/ }, +/* 90 */ +/***/ function(module, exports, __webpack_require__) { - return ForceAtlas2BasedCentralGravitySolver; - })(_CentralGravitySolver3["default"]); + /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; - exports["default"] = ForceAtlas2BasedCentralGravitySolver; - module.exports = exports["default"]; + /* WEBPACK VAR INJECTION */}.call(exports, {})) /***/ }, -/* 85 */ +/* 91 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37170,6 +37399,8 @@ return /******/ (function(modules) { // webpackBootstrap 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) { desc = parent = getter = undefined; _again = false; var object = _x, property = _x2, receiver = _x3; 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); } } }; @@ -37180,35 +37411,95 @@ return /******/ (function(modules) { // webpackBootstrap 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__(74); - - var _Node3 = _interopRequireDefault(_Node2); + var _utilNodeBase = __webpack_require__(108); - /** - * - */ + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - var Cluster = (function (_Node) { - function Cluster(options, body, imagelist, grouplist, globalOptions) { - _classCallCheck(this, Cluster); + 'use strict'; - _get(Object.getPrototypeOf(Cluster.prototype), 'constructor', this).call(this, options, body, imagelist, grouplist, globalOptions); + var Box = (function (_NodeBase) { + function Box(options, body, labelModule) { + _classCallCheck(this, Box); - this.isCluster = true; - this.containedNodes = {}; - this.containedEdges = {}; + _get(Object.getPrototypeOf(Box.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Cluster, _Node); + _inherits(Box, _NodeBase); - return Cluster; - })(_Node3['default']); + _createClass(Box, [{ + key: 'resize', + value: function resize(ctx, selected) { + if (this.width === undefined) { + var margin = 5; + var textSize = this.labelModule.getTextSize(ctx, selected); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; + this.radius = 0.5 * this.width; + } + } + }, { + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx, selected); + this.left = x - this.width / 2; + this.top = y - this.height / 2; - exports['default'] = Cluster; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.lineWidth = selected ? selectionLineWidth : borderWidth; + ctx.lineWidth /= this.body.view.scale; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + + var borderRadius = 6; + ctx.roundRect(this.left, this.top, this.width, this.height, borderRadius); + + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); + + // disable shadows for other elements. + this.disableShadow(ctx); + + ctx.stroke(); + + this.updateBoundingBox(x, y); + this.labelModule.draw(ctx, x, y, selected); + } + }, { + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + + this.boundingBox.left = this.left; + this.boundingBox.top = this.top; + this.boundingBox.bottom = this.top + this.height; + this.boundingBox.right = this.left + this.width; + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); + } + }]); + + return Box; + })(_utilNodeBase2['default']); + + exports['default'] = Box; module.exports = exports['default']; /***/ }, -/* 86 */ +/* 92 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37219,266 +37510,198 @@ return /******/ (function(modules) { // webpackBootstrap 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 _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; 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); } } }; - var util = __webpack_require__(1); - var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(48); - var keycharm = __webpack_require__(88); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - var NavigationHandler = (function () { - function NavigationHandler(body, canvas) { - var _this = this; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - _classCallCheck(this, NavigationHandler); + 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; } - this.body = body; - this.canvas = canvas; + var _utilCircleImageBase = __webpack_require__(109); - this.iconsCreated = false; - this.navigationHammers = []; - this.boundFunctions = {}; - this.touchTime = 0; - this.activated = false; + var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); - this.body.emitter.on('release', function () { - _this._stopMovement(); - }); - this.body.emitter.on('activate', function () { - _this.activated = true;_this.configureKeyboardBindings(); - }); - this.body.emitter.on('deactivate', function () { - _this.activated = false;_this.configureKeyboardBindings(); - }); - this.body.emitter.on('destroy', function () { - if (_this.keycharm !== undefined) { - _this.keycharm.destroy(); - } - }); + 'use strict'; - this.options = {}; + var Circle = (function (_CircleImageBase) { + function Circle(options, body, labelModule) { + _classCallCheck(this, Circle); + + _get(Object.getPrototypeOf(Circle.prototype), 'constructor', this).call(this, options, body, labelModule); } - _createClass(NavigationHandler, [{ - key: 'setOptions', - value: function setOptions(options) { - if (options !== undefined) { - this.options = options; - this.create(); - } - } - }, { - key: 'create', - value: function create() { - if (this.options.navigationButtons === true) { - if (this.iconsCreated === false) { - this.loadNavigationElements(); - } - } else if (this.iconsCreated === true) { - this.cleanNavigation(); - } + _inherits(Circle, _CircleImageBase); - this.configureKeyboardBindings(); + _createClass(Circle, [{ + key: 'resize', + value: function resize(ctx, selected) { + if (this.width === undefined) { + var margin = 5; + var textSize = this.labelModule.getTextSize(ctx, selected); + var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; + this.options.size = diameter / 2; + + this.width = diameter; + this.height = diameter; + this.radius = 0.5 * this.width; + } } }, { - key: 'cleanNavigation', - value: function cleanNavigation() { - // clean hammer bindings - if (this.navigationHammers.length != 0) { - for (var i = 0; i < this.navigationHammers.length; i++) { - this.navigationHammers[i].destroy(); - } - this.navigationHammers = []; - } + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx, selected); + this.left = x - this.width / 2; + this.top = y - this.height / 2; - this._navigationReleaseOverload = function () {}; + this._drawRawCircle(ctx, x, y, selected, hover, this.options.size); - // clean up previous navigation items - if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) { - this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']); - } + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; - this.iconsCreated = false; + this.updateBoundingBox(x, y); + this.labelModule.draw(ctx, x, y, selected); } }, { - key: 'loadNavigationElements', + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); + } + }]); - /** - * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation - * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent - * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. - * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. - * - * @private - */ - value: function loadNavigationElements() { - this.cleanNavigation(); + return Circle; + })(_utilCircleImageBase2['default']); - this.navigationDOM = {}; - var navigationDivs = ['up', 'down', 'left', 'right', 'zoomIn', 'zoomOut', 'zoomExtends']; - var navigationDivActions = ['_moveUp', '_moveDown', '_moveLeft', '_moveRight', '_zoomIn', '_zoomOut', '_fit']; + exports['default'] = Circle; + module.exports = exports['default']; - this.navigationDOM['wrapper'] = document.createElement('div'); - this.navigationDOM['wrapper'].className = 'vis-navigation'; - this.canvas.frame.appendChild(this.navigationDOM['wrapper']); +/***/ }, +/* 93 */ +/***/ function(module, exports, __webpack_require__) { - for (var i = 0; i < navigationDivs.length; i++) { - this.navigationDOM[navigationDivs[i]] = document.createElement('div'); - this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i]; - this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]); + 'use strict'; - var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); - if (navigationDivActions[i] === '_fit') { - hammerUtil.onTouch(hammer, this._fit.bind(this)); - } else { - hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i])); - } + Object.defineProperty(exports, '__esModule', { + value: true + }); - this.navigationHammers.push(hammer); - } + 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; }; })(); - this.iconsCreated = true; - } - }, { - key: 'bindToRedraw', - 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'); - } - } - }, { - key: 'unbindFromRedraw', - value: function unbindFromRedraw(action) { - if (this.boundFunctions[action] !== undefined) { - this.body.emitter.off('initRedraw', this.boundFunctions[action]); - this.body.emitter.emit('_stopRendering'); - delete this.boundFunctions[action]; - } - } - }, { - key: '_fit', + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; 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); } } }; - /** - * this stops all movement induced by the navigation buttons - * - * @private - */ - 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.touchTime = new Date().valueOf(); - } - } - }, { - key: '_stopMovement', + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - /** - * this stops all movement induced by the navigation buttons - * - * @private - */ - 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'); + 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; } + + var _utilCircleImageBase = __webpack_require__(109); + + var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); + + 'use strict'; + + var CircularImage = (function (_CircleImageBase) { + function CircularImage(options, body, labelModule, imageObj) { + _classCallCheck(this, CircularImage); + + _get(Object.getPrototypeOf(CircularImage.prototype), 'constructor', this).call(this, options, body, labelModule); + this.imageObj = imageObj; + this._swapToImageResizeWhenImageLoaded = true; + } + + _inherits(CircularImage, _CircleImageBase); + + _createClass(CircularImage, [{ + key: 'resize', + value: function resize() { + if (this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined) { + if (!this.width) { + var diameter = this.options.size * 2; + this.width = diameter; + this.height = diameter; + this._swapToImageResizeWhenImageLoaded = true; + this.radius = 0.5 * this.width; + } + } else { + if (this._swapToImageResizeWhenImageLoaded) { + this.width = undefined; + this.height = undefined; + this._swapToImageResizeWhenImageLoaded = false; } + this._resizeImage(); } - this.boundFunctions = {}; - } - }, { - key: '_moveUp', - value: function _moveUp() { - this.body.view.translation.y += this.options.keyboard.speed.y; - } - }, { - key: '_moveDown', - value: function _moveDown() { - this.body.view.translation.y -= this.options.keyboard.speed.y; - } - }, { - key: '_moveLeft', - value: function _moveLeft() { - this.body.view.translation.x += this.options.keyboard.speed.x; - } - }, { - key: '_moveRight', - value: function _moveRight() { - this.body.view.translation.x -= this.options.keyboard.speed.x; } }, { - key: '_zoomIn', - value: function _zoomIn() { - this.body.view.scale *= 1 + this.options.keyboard.speed.zoom; - } - }, { - key: '_zoomOut', - value: function _zoomOut() { - this.body.view.scale /= 1 + this.options.keyboard.speed.zoom; - } - }, { - key: 'configureKeyboardBindings', + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(); - /** - * bind all keys using keycharm. - */ - value: function configureKeyboardBindings() { - if (this.keycharm !== undefined) { - this.keycharm.destroy(); - } + this.left = x - this.width / 2; + this.top = y - this.height / 2; - if (this.options.keyboard.enabled === true) { + var size = Math.min(0.5 * this.height, 0.5 * this.width); + this._drawRawCircle(ctx, x, y, selected, hover, size); - if (this.options.keyboard.bindToWindow === true) { - this.keycharm = keycharm({ container: window, preventDefault: true }); - } else { - this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true }); - } + ctx.save(); + ctx.circle(x, y, size); + ctx.stroke(); + ctx.clip(); - this.keycharm.reset(); + this._drawImageAtPosition(ctx); - if (this.activated === true) { - this.keycharm.bind('up', this.bindToRedraw.bind(this, '_moveUp'), 'keydown'); - this.keycharm.bind('down', this.bindToRedraw.bind(this, '_moveDown'), 'keydown'); - this.keycharm.bind('left', this.bindToRedraw.bind(this, '_moveLeft'), 'keydown'); - this.keycharm.bind('right', this.bindToRedraw.bind(this, '_moveRight'), 'keydown'); - this.keycharm.bind('=', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('num+', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('num-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this.keycharm.bind('-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this.keycharm.bind('[', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this.keycharm.bind(']', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('pageup', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('pagedown', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + ctx.restore(); - this.keycharm.bind('up', this.unbindFromRedraw.bind(this, '_moveUp'), 'keyup'); - this.keycharm.bind('down', this.unbindFromRedraw.bind(this, '_moveDown'), 'keyup'); - this.keycharm.bind('left', this.unbindFromRedraw.bind(this, '_moveLeft'), 'keyup'); - this.keycharm.bind('right', this.unbindFromRedraw.bind(this, '_moveRight'), 'keyup'); - this.keycharm.bind('=', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('num+', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('num-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - this.keycharm.bind('-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - this.keycharm.bind('[', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - this.keycharm.bind(']', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('pageup', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('pagedown', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - } - } + this._drawImageLabel(ctx, x, y, selected); + + this.updateBoundingBox(x, y); + } + }, { + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + return this._distanceToBorder(angle); } }]); - return NavigationHandler; - })(); + return CircularImage; + })(_utilCircleImageBase2['default']); - exports['default'] = NavigationHandler; + exports['default'] = CircularImage; module.exports = exports['default']; /***/ }, -/* 87 */ +/* 94 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37489,334 +37712,225 @@ return /******/ (function(modules) { // webpackBootstrap 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) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; 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'); } } - /** - * Popup is a class to create a popup window with some text - * @param {Element} container The container object. - * @param {Number} [x] - * @param {Number} [y] - * @param {String} [text] - * @param {Object} [style] An object containing borderColor, - * backgroundColor, etc. - */ + 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 Popup = (function () { - function Popup(container) { - _classCallCheck(this, Popup); + var _utilNodeBase = __webpack_require__(108); - this.container = container; + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - this.x = 0; - this.y = 0; - this.padding = 5; - this.hidden = false; + 'use strict'; - // create the frame - this.frame = document.createElement('div'); - this.frame.className = 'vis-network-tooltip'; - this.container.appendChild(this.frame); - } + var Database = (function (_NodeBase) { + function Database(options, body, labelModule) { + _classCallCheck(this, Database); - _createClass(Popup, [{ - key: 'setPosition', + _get(Object.getPrototypeOf(Database.prototype), 'constructor', this).call(this, options, body, labelModule); + } - /** - * @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', + _inherits(Database, _NodeBase); - /** - * Set the content for the popup window. This can be HTML code or text. - * @param {string | Element} content - */ - value: function setText(content) { - if (content instanceof Element) { - this.frame.innerHTML = ''; - this.frame.appendChild(content); - } else { - this.frame.innerHTML = content; // string containing text or HTML + _createClass(Database, [{ + key: 'resize', + value: function resize(ctx, selected) { + if (this.width === undefined) { + var margin = 5; + var textSize = this.labelModule.getTextSize(ctx, selected); + var size = textSize.width + 2 * margin; + this.width = size; + this.height = size; + this.radius = 0.5 * this.width; } } }, { - key: 'show', + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx, selected); + this.left = x - this.width / 2; + this.top = y - this.height / 2; - /** - * Show the popup window - * @param {boolean} [doShow] Show or hide the window - */ - value: function show(doShow) { - if (doShow === undefined) { - doShow = true; - } + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - if (doShow === true) { - var height = this.frame.clientHeight; - var width = this.frame.clientWidth; - var maxHeight = this.frame.parentNode.clientHeight; - var maxWidth = this.frame.parentNode.clientWidth; + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.lineWidth = this.selected ? selectionLineWidth : borderWidth; + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - var top = this.y - height; - if (top + height + this.padding > maxHeight) { - top = maxHeight - height - this.padding; - } - if (top < this.padding) { - top = this.padding; - } + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx.database(x - this.width / 2, y - this.height * 0.5, this.width, this.height); - var left = this.x; - if (left + width + this.padding > maxWidth) { - left = maxWidth - width - this.padding; - } - if (left < this.padding) { - left = this.padding; - } + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); - this.frame.style.left = left + 'px'; - this.frame.style.top = top + 'px'; - this.frame.style.visibility = 'visible'; - this.hidden = false; - } else { - this.hide(); - } + // disable shadows for other elements. + this.disableShadow(ctx); + + ctx.stroke(); + + this.updateBoundingBox(x, y); + + this.labelModule.draw(ctx, x, y, selected); } }, { - key: 'hide', + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; - /** - * Hide the popup window - */ - value: function hide() { - this.hidden = true; - this.frame.style.visibility = 'hidden'; + this.boundingBox.left = this.left; + this.boundingBox.top = this.top; + this.boundingBox.bottom = this.top + this.height; + this.boundingBox.right = this.left + this.width; + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); } }]); - return Popup; - })(); + return Database; + })(_utilNodeBase2['default']); - exports['default'] = Popup; + exports['default'] = Database; module.exports = exports['default']; /***/ }, -/* 88 */ +/* 95 */ /***/ 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. - */ + 'use strict'; - // 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 () { + Object.defineProperty(exports, '__esModule', { + value: true + }); - function keycharm(options) { - var preventDefault = options && options.preventDefault || false; + 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 container = options && options.container || window; - var _exportFunctions = {}; - var _bound = {keydown:{}, keyup:{}}; - var _keys = {}; - var i; + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; 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); } } }; - // 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};} + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - // 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}; + 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; } + var _utilShapeBase = __webpack_require__(110); - var down = function(event) {handleEvent(event,'keydown');}; - var up = function(event) {handleEvent(event,'keyup');}; + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - // 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); - } - } + 'use strict'; - if (preventDefault == true) { - event.preventDefault(); - } - } - }; + var Diamond = (function (_ShapeBase) { + function Diamond(options, body, labelModule) { + _classCallCheck(this, Diamond); - // 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}); - }; + _get(Object.getPrototypeOf(Diamond.prototype), 'constructor', this).call(this, options, body, labelModule); + } + _inherits(Diamond, _ShapeBase); - // 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); - } - } - }; + _createClass(Diamond, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); + } + }, { + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'diamond', 4, x, y, selected, hover); + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this._distanceToBorder(angle); + } + }]); - // 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"; - }; + return Diamond; + })(_utilShapeBase2['default']); - // 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] = []; - } - }; + exports['default'] = Diamond; + module.exports = exports['default']; - // reset all bound variables. - _exportFunctions.reset = function() { - _bound = {keydown:{}, keyup:{}}; - }; +/***/ }, +/* 96 */ +/***/ function(module, exports, __webpack_require__) { - // unbind all listeners and reset all variables. - _exportFunctions.destroy = function() { - _bound = {keydown:{}, keyup:{}}; - container.removeEventListener('keydown', down, true); - container.removeEventListener('keyup', up, true); - }; + 'use strict'; - // create listeners. - container.addEventListener('keydown',down,true); - container.addEventListener('keyup',up,true); + Object.defineProperty(exports, '__esModule', { + value: true + }); - // return the public functions. - return _exportFunctions; - } + 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; }; })(); - return keycharm; - })); + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; 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) subClass.__proto__ = superClass; } -/***/ }, -/* 89 */ -/***/ function(module, exports, __webpack_require__) { + var _utilShapeBase = __webpack_require__(110); - function webpackContext(req) { - throw new Error("Cannot find module '" + req + "'."); - } - webpackContext.keys = function() { return []; }; - webpackContext.resolve = webpackContext; - module.exports = webpackContext; - webpackContext.id = 89; + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + + 'use strict'; + + var Dot = (function (_ShapeBase) { + function Dot(options, body, labelModule) { + _classCallCheck(this, Dot); + + _get(Object.getPrototypeOf(Dot.prototype), 'constructor', this).call(this, options, body, labelModule); + } + + _inherits(Dot, _ShapeBase); + + _createClass(Dot, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); + } + }, { + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'circle', 2, x, y, selected, hover); + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this.options.size + this.options.borderWidth; + } + }]); + + return Dot; + })(_utilShapeBase2['default']); + exports['default'] = Dot; + module.exports = exports['default']; /***/ }, -/* 90 */ +/* 97 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37837,29 +37951,32 @@ return /******/ (function(modules) { // webpackBootstrap 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__(109); + var _utilNodeBase = __webpack_require__(108); var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); 'use strict'; - var Box = (function (_NodeBase) { - function Box(options, body, labelModule) { - _classCallCheck(this, Box); + var Ellipse = (function (_NodeBase) { + function Ellipse(options, body, labelModule) { + _classCallCheck(this, Ellipse); - _get(Object.getPrototypeOf(Box.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Ellipse.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Box, _NodeBase); + _inherits(Ellipse, _NodeBase); - _createClass(Box, [{ + _createClass(Ellipse, [{ key: 'resize', value: function resize(ctx, selected) { if (this.width === undefined) { - var margin = 5; var textSize = this.labelModule.getTextSize(ctx, selected); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; + + this.width = textSize.width * 1.5; + this.height = textSize.height * 2; + if (this.width < this.height) { + this.width = this.height; + } this.radius = 0.5 * this.width; } } @@ -37867,21 +37984,20 @@ return /******/ (function(modules) { // webpackBootstrap key: 'draw', value: function draw(ctx, x, y, selected, hover) { this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; var borderWidth = this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.lineWidth = selected ? selectionLineWidth : borderWidth; ctx.lineWidth /= this.body.view.scale; ctx.lineWidth = Math.min(this.width, ctx.lineWidth); ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - - var borderRadius = 6; - ctx.roundRect(this.left, this.top, this.width, this.height, borderRadius); + ctx.ellipse(this.left, this.top, this.width, this.height); // draw shadow if enabled this.enableShadow(ctx); @@ -37910,22 +38026,22 @@ return /******/ (function(modules) { // webpackBootstrap key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; + var a = this.width * 0.5; + var b = this.height * 0.5; var w = Math.sin(angle) * a; var h = Math.cos(angle) * b; return a * b / Math.sqrt(w * w + h * h); } }]); - return Box; + return Ellipse; })(_utilNodeBase2['default']); - exports['default'] = Box; + exports['default'] = Ellipse; module.exports = exports['default']; /***/ }, -/* 91 */ +/* 98 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37946,80 +38062,106 @@ return /******/ (function(modules) { // webpackBootstrap 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__(110); + var _utilNodeBase = __webpack_require__(108); - var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); 'use strict'; - var Circle = (function (_CircleImageBase) { - function Circle(options, body, labelModule) { - _classCallCheck(this, Circle); + var Icon = (function (_NodeBase) { + function Icon(options, body, labelModule) { + _classCallCheck(this, Icon); - _get(Object.getPrototypeOf(Circle.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Icon.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Circle, _CircleImageBase); + _inherits(Icon, _NodeBase); - _createClass(Circle, [{ + _createClass(Icon, [{ key: 'resize', - value: function resize(ctx, selected) { + value: function resize(ctx) { if (this.width === undefined) { var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; - this.options.size = diameter / 2; - - this.width = diameter; - this.height = diameter; + var iconSize = { + width: Number(this.options.icon.size), + height: Number(this.options.icon.size) + }; + this.width = iconSize.width + 2 * margin; + this.height = iconSize.height + 2 * margin; this.radius = 0.5 * this.width; } } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; + this.resize(ctx); + this.options.icon.size = this.options.icon.size || 50; - this._drawRawCircle(ctx, x, y, selected, hover, this.options.size); + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + this._icon(ctx, x, y, selected); - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; + if (this.options.label !== undefined) { + var iconTextSpacing = 5; + this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected); + } this.updateBoundingBox(x, y); - this.labelModule.draw(ctx, x, y, selected); } }, { key: 'updateBoundingBox', value: function updateBoundingBox(x, y) { - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; + this.boundingBox.top = y - this.options.icon.size * 0.5; + this.boundingBox.left = x - this.options.icon.size * 0.5; + this.boundingBox.right = x + this.options.icon.size * 0.5; + this.boundingBox.bottom = y + this.options.icon.size * 0.5; + + if (this.options.label !== undefined) { + var iconTextSpacing = 5; + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing); + } + } + }, { + key: '_icon', + value: function _icon(ctx, x, y, selected) { + var iconSize = Number(this.options.icon.size); + + if (this.options.icon.code !== undefined) { + 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'; + + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fillText(this.options.icon.code, x, y); + + // disable shadows for other elements. + this.disableShadow(ctx); + } else { + console.error('When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.'); + } } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); + this._distanceToBorder(angle); } }]); - return Circle; - })(_utilCircleImageBase2['default']); + return Icon; + })(_utilNodeBase2['default']); - exports['default'] = Circle; + exports['default'] = Icon; module.exports = exports['default']; /***/ }, -/* 92 */ +/* 99 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38040,94 +38182,77 @@ return /******/ (function(modules) { // webpackBootstrap 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__(110); + var _utilCircleImageBase = __webpack_require__(109); var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); 'use strict'; - var CircularImage = (function (_CircleImageBase) { - function CircularImage(options, body, labelModule, imageObj) { - _classCallCheck(this, CircularImage); + var Image = (function (_CircleImageBase) { + function Image(options, body, labelModule, imageObj) { + _classCallCheck(this, Image); - _get(Object.getPrototypeOf(CircularImage.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Image.prototype), 'constructor', this).call(this, options, body, labelModule); this.imageObj = imageObj; - this._swapToImageResizeWhenImageLoaded = true; } - _inherits(CircularImage, _CircleImageBase); + _inherits(Image, _CircleImageBase); - _createClass(CircularImage, [{ + _createClass(Image, [{ key: 'resize', value: function resize() { - if (this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined) { - if (!this.width) { - var diameter = this.options.size * 2; - this.width = diameter; - this.height = diameter; - this._swapToImageResizeWhenImageLoaded = true; - this.radius = 0.5 * this.width; - } - } else { - if (this._swapToImageResizeWhenImageLoaded) { - this.width = undefined; - this.height = undefined; - this._swapToImageResizeWhenImageLoaded = false; - } - this._resizeImage(); - } + this._resizeImage(); } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { this.resize(); - this.left = x - this.width / 2; this.top = y - this.height / 2; - var size = Math.min(0.5 * this.height, 0.5 * this.width); - this._drawRawCircle(ctx, x, y, selected, hover, size); - - ctx.save(); - ctx.circle(x, y, size); - ctx.stroke(); - ctx.clip(); - this._drawImageAtPosition(ctx); - ctx.restore(); - - this._drawImageLabel(ctx, x, y, selected); + this._drawImageLabel(ctx, x, y, selected || hover); this.updateBoundingBox(x, y); } }, { key: 'updateBoundingBox', value: function updateBoundingBox(x, y) { - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); + this.left = x - this.width / 2; + this.top = y - this.height / 2; + + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; + + if (this.options.label !== undefined) { + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); + } } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { this.resize(ctx); - return this._distanceToBorder(angle); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); } }]); - return CircularImage; + return Image; })(_utilCircleImageBase2['default']); - exports['default'] = CircularImage; + exports['default'] = Image; module.exports = exports['default']; /***/ }, -/* 93 */ +/* 100 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38148,95 +38273,47 @@ return /******/ (function(modules) { // webpackBootstrap 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__(109); + var _utilShapeBase = __webpack_require__(110); - var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); 'use strict'; - var Database = (function (_NodeBase) { - function Database(options, body, labelModule) { - _classCallCheck(this, Database); + var Square = (function (_ShapeBase) { + function Square(options, body, labelModule) { + _classCallCheck(this, Square); - _get(Object.getPrototypeOf(Database.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Square.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Database, _NodeBase); + _inherits(Square, _ShapeBase); - _createClass(Database, [{ + _createClass(Square, [{ key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - var size = textSize.width + 2 * margin; - this.width = size; - this.height = size; - this.radius = 0.5 * this.width; - } + value: function resize() { + this._resizeShape(); } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = this.selected ? selectionLineWidth : borderWidth; - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx.database(x - this.width / 2, y - this.height * 0.5, this.width, this.height); - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); - - // disable shadows for other elements. - this.disableShadow(ctx); - - ctx.stroke(); - - this.updateBoundingBox(x, y); - - this.labelModule.draw(ctx, x, y, selected); - } - }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.left = x - this.width * 0.5; - this.top = y - this.height * 0.5; - - this.boundingBox.left = this.left; - this.boundingBox.top = this.top; - this.boundingBox.bottom = this.top + this.height; - this.boundingBox.right = this.left + this.width; + this._drawShape(ctx, 'square', 2, x, y, selected, hover); } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); + return this._distanceToBorder(angle); } }]); - return Database; - })(_utilNodeBase2['default']); + return Square; + })(_utilShapeBase2['default']); - exports['default'] = Database; + exports['default'] = Square; module.exports = exports['default']; /***/ }, -/* 94 */ +/* 101 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38257,22 +38334,22 @@ return /******/ (function(modules) { // webpackBootstrap 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__(111); + var _utilShapeBase = __webpack_require__(110); var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); 'use strict'; - var Diamond = (function (_ShapeBase) { - function Diamond(options, body, labelModule) { - _classCallCheck(this, Diamond); + var Star = (function (_ShapeBase) { + function Star(options, body, labelModule) { + _classCallCheck(this, Star); - _get(Object.getPrototypeOf(Diamond.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Star.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Diamond, _ShapeBase); + _inherits(Star, _ShapeBase); - _createClass(Diamond, [{ + _createClass(Star, [{ key: 'resize', value: function resize(ctx) { this._resizeShape(); @@ -38280,7 +38357,7 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'diamond', 4, x, y, selected, hover); + this._drawShape(ctx, 'star', 4, x, y, selected, hover); } }, { key: 'distanceToBorder', @@ -38289,14 +38366,14 @@ return /******/ (function(modules) { // webpackBootstrap } }]); - return Diamond; + return Star; })(_utilShapeBase2['default']); - exports['default'] = Diamond; + exports['default'] = Star; module.exports = exports['default']; /***/ }, -/* 95 */ +/* 102 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38317,46 +38394,75 @@ return /******/ (function(modules) { // webpackBootstrap 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__(111); + var _utilNodeBase = __webpack_require__(108); - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); 'use strict'; - var Dot = (function (_ShapeBase) { - function Dot(options, body, labelModule) { - _classCallCheck(this, Dot); + var Text = (function (_NodeBase) { + function Text(options, body, labelModule) { + _classCallCheck(this, Text); - _get(Object.getPrototypeOf(Dot.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Text.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Dot, _ShapeBase); + _inherits(Text, _NodeBase); - _createClass(Dot, [{ + _createClass(Text, [{ key: 'resize', - value: function resize(ctx) { - this._resizeShape(); + value: function resize(ctx, selected) { + if (this.width === undefined) { + var margin = 5; + var textSize = this.labelModule.getTextSize(ctx, selected); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; + this.radius = 0.5 * this.width; + } } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'circle', 2, x, y, selected, hover); + this.resize(ctx, selected || hover); + this.left = x - this.width / 2; + this.top = y - this.height / 2; + + // draw shadow if enabled + this.enableShadow(ctx); + this.labelModule.draw(ctx, x, y, selected || hover); + + // disable shadows for other elements. + this.disableShadow(ctx); + + this.updateBoundingBox(x, y); + } + }, { + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width / 2; + this.top = y - this.height / 2; + + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { - return this.options.size + this.options.borderWidth; + this.resize(ctx); + return this._distanceToBorder(angle); } }]); - return Dot; - })(_utilShapeBase2['default']); + return Text; + })(_utilNodeBase2['default']); - exports['default'] = Dot; + exports['default'] = Text; module.exports = exports['default']; /***/ }, -/* 96 */ +/* 103 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38377,97 +38483,46 @@ return /******/ (function(modules) { // webpackBootstrap 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__(109); + var _utilShapeBase = __webpack_require__(110); - var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); 'use strict'; - var Ellipse = (function (_NodeBase) { - function Ellipse(options, body, labelModule) { - _classCallCheck(this, Ellipse); + var Triangle = (function (_ShapeBase) { + function Triangle(options, body, labelModule) { + _classCallCheck(this, Triangle); - _get(Object.getPrototypeOf(Ellipse.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Triangle.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Ellipse, _NodeBase); + _inherits(Triangle, _ShapeBase); - _createClass(Ellipse, [{ + _createClass(Triangle, [{ key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var textSize = this.labelModule.getTextSize(ctx, selected); - - this.width = textSize.width * 1.5; - this.height = textSize.height * 2; - if (this.width < this.height) { - this.width = this.height; - } - this.radius = 0.5 * this.width; - } + value: function resize(ctx) { + this._resizeShape(); } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width * 0.5; - this.top = y - this.height * 0.5; - - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - - ctx.lineWidth = selected ? selectionLineWidth : borderWidth; - ctx.lineWidth /= this.body.view.scale; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx.ellipse(this.left, this.top, this.width, this.height); - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); - - // disable shadows for other elements. - this.disableShadow(ctx); - - ctx.stroke(); - - this.updateBoundingBox(x, y); - this.labelModule.draw(ctx, x, y, selected); - } - }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.left = x - this.width * 0.5; - this.top = y - this.height * 0.5; - - this.boundingBox.left = this.left; - this.boundingBox.top = this.top; - this.boundingBox.bottom = this.top + this.height; - this.boundingBox.right = this.left + this.width; + this._drawShape(ctx, 'triangle', 3, x, y, selected, hover); } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width * 0.5; - var b = this.height * 0.5; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); + return this._distanceToBorder(angle); } }]); - return Ellipse; - })(_utilNodeBase2['default']); + return Triangle; + })(_utilShapeBase2['default']); - exports['default'] = Ellipse; + exports['default'] = Triangle; module.exports = exports['default']; /***/ }, -/* 97 */ +/* 104 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38488,106 +38543,46 @@ return /******/ (function(modules) { // webpackBootstrap 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__(109); + var _utilShapeBase = __webpack_require__(110); - var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); 'use strict'; - var Icon = (function (_NodeBase) { - function Icon(options, body, labelModule) { - _classCallCheck(this, Icon); + var TriangleDown = (function (_ShapeBase) { + function TriangleDown(options, body, labelModule) { + _classCallCheck(this, TriangleDown); - _get(Object.getPrototypeOf(Icon.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(TriangleDown.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Icon, _NodeBase); + _inherits(TriangleDown, _ShapeBase); - _createClass(Icon, [{ + _createClass(TriangleDown, [{ key: 'resize', value: function resize(ctx) { - if (this.width === undefined) { - var margin = 5; - var iconSize = { - width: Number(this.options.icon.size), - height: Number(this.options.icon.size) - }; - this.width = iconSize.width + 2 * margin; - this.height = iconSize.height + 2 * margin; - this.radius = 0.5 * this.width; - } + this._resizeShape(); } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx); - this.options.icon.size = this.options.icon.size || 50; - - this.left = x - this.width * 0.5; - this.top = y - this.height * 0.5; - this._icon(ctx, x, y, selected); - - if (this.options.label !== undefined) { - var iconTextSpacing = 5; - this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected); - } - - this.updateBoundingBox(x, y); - } - }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.boundingBox.top = y - this.options.icon.size * 0.5; - this.boundingBox.left = x - this.options.icon.size * 0.5; - this.boundingBox.right = x + this.options.icon.size * 0.5; - this.boundingBox.bottom = y + this.options.icon.size * 0.5; - - if (this.options.label !== undefined) { - var iconTextSpacing = 5; - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing); - } - } - }, { - key: '_icon', - value: function _icon(ctx, x, y, selected) { - var iconSize = Number(this.options.icon.size); - - if (this.options.icon.code !== undefined) { - 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'; - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fillText(this.options.icon.code, x, y); - - // disable shadows for other elements. - this.disableShadow(ctx); - } else { - console.error('When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.'); - } + this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover); } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - this._distanceToBorder(angle); + return this._distanceToBorder(angle); } }]); - return Icon; - })(_utilNodeBase2['default']); + return TriangleDown; + })(_utilShapeBase2['default']); - exports['default'] = Icon; + exports['default'] = TriangleDown; module.exports = exports['default']; /***/ }, -/* 98 */ +/* 105 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38608,198 +38603,144 @@ return /******/ (function(modules) { // webpackBootstrap 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__(110); - - var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); + var _utilBezierEdgeBase = __webpack_require__(111); - 'use strict'; + var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); - var Image = (function (_CircleImageBase) { - function Image(options, body, labelModule, imageObj) { - _classCallCheck(this, Image); + var BezierEdgeDynamic = (function (_BezierEdgeBase) { + function BezierEdgeDynamic(options, body, labelModule) { + _classCallCheck(this, BezierEdgeDynamic); - _get(Object.getPrototypeOf(Image.prototype), 'constructor', this).call(this, options, body, labelModule); - this.imageObj = imageObj; + //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked. + _get(Object.getPrototypeOf(BezierEdgeDynamic.prototype), 'constructor', this).call(this, options, body, labelModule); // --> this calls the setOptions below } - _inherits(Image, _CircleImageBase); - - _createClass(Image, [{ - key: 'resize', - value: function resize() { - this._resizeImage(); - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(); - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - this._drawImageAtPosition(ctx); + _inherits(BezierEdgeDynamic, _BezierEdgeBase); - this._drawImageLabel(ctx, x, y, selected || hover); + _createClass(BezierEdgeDynamic, [{ + key: 'setOptions', + value: function setOptions(options) { + this.options = options; + this.from = this.body.nodes[this.options.from]; + this.to = this.body.nodes[this.options.to]; + this.id = this.options.id; + this.setupSupportNode(); - this.updateBoundingBox(x, y); + // fix weird behaviour + if (this.from.id === this.to.id) { + this.via.setOptions({ physics: false }); + } else { + this.via.setOptions({ physics: true }); + } } }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - - if (this.options.label !== undefined) { - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); + key: 'cleanup', + value: function cleanup() { + if (this.via !== undefined) { + delete this.body.nodes[this.via.id]; + this.via = undefined; + return true; } + return false; } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); + key: 'togglePhysics', + value: function togglePhysics(status) { + this.via.setOptions({ physics: status }); } - }]); - - return Image; - })(_utilCircleImageBase2['default']); - - exports['default'] = Image; - module.exports = exports['default']; - -/***/ }, -/* 99 */ -/***/ 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) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; 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) subClass.__proto__ = superClass; } - - var _utilShapeBase = __webpack_require__(111); - - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - - 'use strict'; - - var Square = (function (_ShapeBase) { - function Square(options, body, labelModule) { - _classCallCheck(this, Square); - - _get(Object.getPrototypeOf(Square.prototype), 'constructor', this).call(this, options, body, labelModule); - } - - _inherits(Square, _ShapeBase); + }, { + key: 'setupSupportNode', - _createClass(Square, [{ - key: 'resize', - value: function resize() { - this._resizeShape(); + /** + * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but + * are used for the force calculation. + * + * The changed data is not called, if needed, it is returned by the main edge constructor. + * @private + */ + value: function setupSupportNode() { + if (this.via === undefined) { + var nodeId = 'edgeId:' + this.id; + var node = this.body.functions.createNode({ + id: nodeId, + shape: 'circle', + physics: true, + hidden: true + }); + this.body.nodes[nodeId] = node; + this.via = node; + this.via.parentEdgeId = this.id; + this.positionBezierNode(); + } } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'square', 2, x, y, selected, hover); + key: 'positionBezierNode', + value: function positionBezierNode() { + if (this.via !== undefined && this.from !== undefined && this.to !== undefined) { + this.via.x = 0.5 * (this.from.x + this.to.x); + this.via.y = 0.5 * (this.from.y + this.to.y); + } else if (this.via !== undefined) { + this.via.x = 0; + this.via.y = 0; + } } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - return this._distanceToBorder(angle); - } - }]); - - return Square; - })(_utilShapeBase2['default']); - - exports['default'] = Square; - module.exports = exports['default']; - -/***/ }, -/* 100 */ -/***/ 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) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; 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) subClass.__proto__ = superClass; } - - var _utilShapeBase = __webpack_require__(111); - - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - - 'use strict'; - - var Star = (function (_ShapeBase) { - function Star(options, body, labelModule) { - _classCallCheck(this, Star); + key: '_line', - _get(Object.getPrototypeOf(Star.prototype), 'constructor', this).call(this, options, body, labelModule); - } + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + value: function _line(ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y); + // draw shadow if enabled + this.enableShadow(ctx); + ctx.stroke(); + this.disableShadow(ctx); + return this.via; + } + }, { + key: 'getPoint', - _inherits(Star, _ShapeBase); + /** + * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way + * @param percentage + * @param via + * @returns {{x: number, y: number}} + * @private + */ + 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; + var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * this.via.y + Math.pow(t, 2) * this.to.y; - _createClass(Star, [{ - key: 'resize', - value: function resize(ctx) { - this._resizeShape(); + return { x: x, y: y }; } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'star', 4, x, y, selected, hover); + key: '_findBorderPosition', + value: function _findBorderPosition(nearNode, ctx) { + return this._findBorderPositionBezier(nearNode, ctx, this.via); } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + key: '_getDistanceToEdge', + value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { + // x3,y3 is the point + return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via); } }]); - return Star; - })(_utilShapeBase2['default']); + return BezierEdgeDynamic; + })(_utilBezierEdgeBase2['default']); - exports['default'] = Star; + exports['default'] = BezierEdgeDynamic; module.exports = exports['default']; /***/ }, -/* 101 */ +/* 106 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38810,9 +38751,9 @@ return /******/ (function(modules) { // webpackBootstrap 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) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; 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); } } }; + var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x4, + property = _x5, + receiver = _x6; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = 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 }; } @@ -38820,418 +38761,51 @@ return /******/ (function(modules) { // webpackBootstrap 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__(109); - - var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + var _utilBezierEdgeBase = __webpack_require__(111); - 'use strict'; + var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); - var Text = (function (_NodeBase) { - function Text(options, body, labelModule) { - _classCallCheck(this, Text); + var BezierEdgeStatic = (function (_BezierEdgeBase) { + function BezierEdgeStatic(options, body, labelModule) { + _classCallCheck(this, BezierEdgeStatic); - _get(Object.getPrototypeOf(Text.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(BezierEdgeStatic.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Text, _NodeBase); + _inherits(BezierEdgeStatic, _BezierEdgeBase); - _createClass(Text, [{ - key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; - this.radius = 0.5 * this.width; - } + _createClass(BezierEdgeStatic, [{ + key: 'cleanup', + value: function cleanup() { + return false; } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected || hover); - this.left = x - this.width / 2; - this.top = y - this.height / 2; + key: '_line', + + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + value: function _line(ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + var via = this._getViaCoordinates(); + var returnValue = via; + // fallback to normal straight edges + if (via.x === undefined) { + ctx.lineTo(this.to.x, this.to.y); + returnValue = undefined; + } else { + ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y); + } // draw shadow if enabled this.enableShadow(ctx); - this.labelModule.draw(ctx, x, y, selected || hover); - - // disable shadows for other elements. + ctx.stroke(); this.disableShadow(ctx); - - this.updateBoundingBox(x, y); - } - }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - return this._distanceToBorder(angle); - } - }]); - - return Text; - })(_utilNodeBase2['default']); - - exports['default'] = Text; - module.exports = exports['default']; - -/***/ }, -/* 102 */ -/***/ 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) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; 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) subClass.__proto__ = superClass; } - - var _utilShapeBase = __webpack_require__(111); - - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - - 'use strict'; - - var Triangle = (function (_ShapeBase) { - function Triangle(options, body, labelModule) { - _classCallCheck(this, Triangle); - - _get(Object.getPrototypeOf(Triangle.prototype), 'constructor', this).call(this, options, body, labelModule); - } - - _inherits(Triangle, _ShapeBase); - - _createClass(Triangle, [{ - key: 'resize', - value: function resize(ctx) { - this._resizeShape(); - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'triangle', 3, x, y, selected, hover); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); - } - }]); - - return Triangle; - })(_utilShapeBase2['default']); - - exports['default'] = Triangle; - module.exports = exports['default']; - -/***/ }, -/* 103 */ -/***/ 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) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; 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) subClass.__proto__ = superClass; } - - var _utilShapeBase = __webpack_require__(111); - - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - - 'use strict'; - - var TriangleDown = (function (_ShapeBase) { - function TriangleDown(options, body, labelModule) { - _classCallCheck(this, TriangleDown); - - _get(Object.getPrototypeOf(TriangleDown.prototype), 'constructor', this).call(this, options, body, labelModule); - } - - _inherits(TriangleDown, _ShapeBase); - - _createClass(TriangleDown, [{ - key: 'resize', - value: function resize(ctx) { - this._resizeShape(); - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); - } - }]); - - return TriangleDown; - })(_utilShapeBase2['default']); - - exports['default'] = TriangleDown; - module.exports = exports['default']; - -/***/ }, -/* 104 */ -/***/ 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) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; 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) subClass.__proto__ = superClass; } - - var _utilBezierEdgeBase = __webpack_require__(112); - - var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); - - var BezierEdgeDynamic = (function (_BezierEdgeBase) { - function BezierEdgeDynamic(options, body, labelModule) { - _classCallCheck(this, BezierEdgeDynamic); - - //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked. - _get(Object.getPrototypeOf(BezierEdgeDynamic.prototype), 'constructor', this).call(this, options, body, labelModule); // --> this calls the setOptions below - } - - _inherits(BezierEdgeDynamic, _BezierEdgeBase); - - _createClass(BezierEdgeDynamic, [{ - key: 'setOptions', - value: function setOptions(options) { - this.options = options; - this.from = this.body.nodes[this.options.from]; - this.to = this.body.nodes[this.options.to]; - this.id = this.options.id; - this.setupSupportNode(); - - // fix weird behaviour - if (this.from.id === this.to.id) { - this.via.setOptions({ physics: false }); - } else { - this.via.setOptions({ physics: true }); - } - } - }, { - key: 'cleanup', - value: function cleanup() { - if (this.via !== undefined) { - delete this.body.nodes[this.via.id]; - this.via = undefined; - return true; - } - return false; - } - }, { - key: 'togglePhysics', - value: function togglePhysics(status) { - this.via.setOptions({ physics: status }); - } - }, { - key: 'setupSupportNode', - - /** - * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but - * are used for the force calculation. - * - * The changed data is not called, if needed, it is returned by the main edge constructor. - * @private - */ - value: function setupSupportNode() { - if (this.via === undefined) { - var nodeId = 'edgeId:' + this.id; - var node = this.body.functions.createNode({ - id: nodeId, - shape: 'circle', - physics: true, - hidden: true - }); - this.body.nodes[nodeId] = node; - this.via = node; - this.via.parentEdgeId = this.id; - this.positionBezierNode(); - } - } - }, { - key: 'positionBezierNode', - value: function positionBezierNode() { - if (this.via !== undefined && this.from !== undefined && this.to !== undefined) { - this.via.x = 0.5 * (this.from.x + this.to.x); - this.via.y = 0.5 * (this.from.y + this.to.y); - } else if (this.via !== undefined) { - this.via.x = 0; - this.via.y = 0; - } - } - }, { - key: '_line', - - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _line(ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y); - // draw shadow if enabled - this.enableShadow(ctx); - ctx.stroke(); - 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 - * @param percentage - * @param via - * @returns {{x: number, y: number}} - * @private - */ - 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; - var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * this.via.y + Math.pow(t, 2) * this.to.y; - - return { x: x, y: y }; - } - }, { - key: '_findBorderPosition', - value: function _findBorderPosition(nearNode, ctx) { - return this._findBorderPositionBezier(nearNode, ctx, this.via); - } - }, { - key: '_getDistanceToEdge', - value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { - // x3,y3 is the point - return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via); - } - }]); - - return BezierEdgeDynamic; - })(_utilBezierEdgeBase2['default']); - - exports['default'] = BezierEdgeDynamic; - module.exports = exports['default']; - -/***/ }, -/* 105 */ -/***/ 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(_x4, _x5, _x6) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x4, - property = _x5, - receiver = _x6; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = 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) subClass.__proto__ = superClass; } - - var _utilBezierEdgeBase = __webpack_require__(112); - - var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); - - var BezierEdgeStatic = (function (_BezierEdgeBase) { - function BezierEdgeStatic(options, body, labelModule) { - _classCallCheck(this, BezierEdgeStatic); - - _get(Object.getPrototypeOf(BezierEdgeStatic.prototype), 'constructor', this).call(this, options, body, labelModule); - } - - _inherits(BezierEdgeStatic, _BezierEdgeBase); - - _createClass(BezierEdgeStatic, [{ - key: 'cleanup', - value: function cleanup() { - return false; - } - }, { - key: '_line', - - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _line(ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - var via = this._getViaCoordinates(); - var returnValue = via; - - // fallback to normal straight edges - if (via.x === undefined) { - ctx.lineTo(this.to.x, this.to.y); - returnValue = undefined; - } else { - ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y); - } - // draw shadow if enabled - this.enableShadow(ctx); - ctx.stroke(); - this.disableShadow(ctx); - return returnValue; + return returnValue; } }, { key: '_getViaCoordinates', @@ -39432,7 +39006,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 106 */ +/* 107 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39453,7 +39027,7 @@ return /******/ (function(modules) { // webpackBootstrap 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__(113); + var _utilEdgeBase = __webpack_require__(112); var _utilEdgeBase2 = _interopRequireDefault(_utilEdgeBase); @@ -39543,32 +39117,8 @@ return /******/ (function(modules) { // webpackBootstrap exports['default'] = StraightEdge; module.exports = exports['default']; -/***/ }, -/* 107 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = function(module) { - if(!module.webpackPolyfill) { - module.deprecate = function() {}; - module.paths = []; - // module.parent = undefined by default - module.children = []; - module.webpackPolyfill = 1; - } - return module; - } - - /***/ }, /* 108 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; - - /* WEBPACK VAR INJECTION */}.call(exports, {})) - -/***/ }, -/* 109 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39636,7 +39186,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 110 */ +/* 109 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39657,7 +39207,7 @@ return /******/ (function(modules) { // webpackBootstrap 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__(109); + var _utilNodeBase = __webpack_require__(108); var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); @@ -39765,7 +39315,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 111 */ +/* 110 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39786,7 +39336,7 @@ return /******/ (function(modules) { // webpackBootstrap 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__(109); + var _utilNodeBase = __webpack_require__(108); var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); @@ -39866,7 +39416,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 112 */ +/* 111 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39887,7 +39437,7 @@ return /******/ (function(modules) { // webpackBootstrap 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__(113); + var _EdgeBase2 = __webpack_require__(112); var _EdgeBase3 = _interopRequireDefault(_EdgeBase2); @@ -40015,7 +39565,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 113 */ +/* 112 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -40600,6 +40150,586 @@ return /******/ (function(modules) { // webpackBootstrap exports['default'] = EdgeBase; module.exports = exports['default']; +/***/ }, +/* 113 */ +/***/ 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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + var Hammer = __webpack_require__(41); + var hammerUtil = __webpack_require__(49); + var util = __webpack_require__(1); + + var ColorPicker = (function () { + function ColorPicker() { + var pixelRatio = arguments[0] === undefined ? 1 : arguments[0]; + + _classCallCheck(this, ColorPicker); + + this.pixelRatio = pixelRatio; + this.generated = false; + this.centerCoordinates = { x: 289 / 2, y: 289 / 2 }; + this.r = 289 * 0.49; + this.color = { r: 255, g: 255, b: 255, a: 1 }; + this.hueCircle = undefined; + this.initialColor = { r: 255, g: 255, b: 255, a: 1 }; + this.previousColor = undefined; + this.applied = false; + + // bound by + this.updateCallback = function () {}; + + // create all DOM elements + this._create(); + } + + _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(); + this.hammer = undefined; + } + this.container = container; + this.container.appendChild(this.frame); + this._bindHammer(); + + this._setSize(); + } + }, { + key: 'setCallback', + + /** + * the callback is executed on apply and save. Bind it to the application + * @param callback + */ + 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.'); + } + } + }, { + key: '_isColorString', + value: function _isColorString(color) { + var htmlColors = { black: '#000000', navy: '#000080', darkblue: '#00008B', mediumblue: '#0000CD', blue: '#0000FF', darkgreen: '#006400', green: '#008000', teal: '#008080', darkcyan: '#008B8B', deepskyblue: '#00BFFF', darkturquoise: '#00CED1', mediumspringgreen: '#00FA9A', lime: '#00FF00', springgreen: '#00FF7F', aqua: '#00FFFF', cyan: '#00FFFF', midnightblue: '#191970', dodgerblue: '#1E90FF', lightseagreen: '#20B2AA', forestgreen: '#228B22', seagreen: '#2E8B57', darkslategray: '#2F4F4F', limegreen: '#32CD32', mediumseagreen: '#3CB371', turquoise: '#40E0D0', royalblue: '#4169E1', steelblue: '#4682B4', darkslateblue: '#483D8B', mediumturquoise: '#48D1CC', indigo: '#4B0082', darkolivegreen: '#556B2F', cadetblue: '#5F9EA0', cornflowerblue: '#6495ED', mediumaquamarine: '#66CDAA', dimgray: '#696969', slateblue: '#6A5ACD', olivedrab: '#6B8E23', slategray: '#708090', lightslategray: '#778899', mediumslateblue: '#7B68EE', lawngreen: '#7CFC00', chartreuse: '#7FFF00', aquamarine: '#7FFFD4', maroon: '#800000', purple: '#800080', olive: '#808000', gray: '#808080', skyblue: '#87CEEB', lightskyblue: '#87CEFA', blueviolet: '#8A2BE2', darkred: '#8B0000', darkmagenta: '#8B008B', saddlebrown: '#8B4513', darkseagreen: '#8FBC8F', lightgreen: '#90EE90', mediumpurple: '#9370D8', darkviolet: '#9400D3', palegreen: '#98FB98', darkorchid: '#9932CC', yellowgreen: '#9ACD32', sienna: '#A0522D', brown: '#A52A2A', darkgray: '#A9A9A9', lightblue: '#ADD8E6', greenyellow: '#ADFF2F', paleturquoise: '#AFEEEE', lightsteelblue: '#B0C4DE', powderblue: '#B0E0E6', firebrick: '#B22222', darkgoldenrod: '#B8860B', mediumorchid: '#BA55D3', rosybrown: '#BC8F8F', darkkhaki: '#BDB76B', silver: '#C0C0C0', mediumvioletred: '#C71585', indianred: '#CD5C5C', peru: '#CD853F', chocolate: '#D2691E', tan: '#D2B48C', lightgrey: '#D3D3D3', palevioletred: '#D87093', thistle: '#D8BFD8', orchid: '#DA70D6', goldenrod: '#DAA520', crimson: '#DC143C', gainsboro: '#DCDCDC', plum: '#DDA0DD', burlywood: '#DEB887', lightcyan: '#E0FFFF', lavender: '#E6E6FA', darksalmon: '#E9967A', violet: '#EE82EE', palegoldenrod: '#EEE8AA', lightcoral: '#F08080', khaki: '#F0E68C', aliceblue: '#F0F8FF', honeydew: '#F0FFF0', azure: '#F0FFFF', sandybrown: '#F4A460', wheat: '#F5DEB3', beige: '#F5F5DC', whitesmoke: '#F5F5F5', mintcream: '#F5FFFA', ghostwhite: '#F8F8FF', salmon: '#FA8072', antiquewhite: '#FAEBD7', linen: '#FAF0E6', lightgoldenrodyellow: '#FAFAD2', oldlace: '#FDF5E6', red: '#FF0000', fuchsia: '#FF00FF', magenta: '#FF00FF', deeppink: '#FF1493', orangered: '#FF4500', tomato: '#FF6347', hotpink: '#FF69B4', coral: '#FF7F50', darkorange: '#FF8C00', lightsalmon: '#FFA07A', orange: '#FFA500', lightpink: '#FFB6C1', pink: '#FFC0CB', gold: '#FFD700', peachpuff: '#FFDAB9', navajowhite: '#FFDEAD', moccasin: '#FFE4B5', bisque: '#FFE4C4', mistyrose: '#FFE4E1', blanchedalmond: '#FFEBCD', papayawhip: '#FFEFD5', lavenderblush: '#FFF0F5', seashell: '#FFF5EE', cornsilk: '#FFF8DC', lemonchiffon: '#FFFACD', floralwhite: '#FFFAF0', snow: '#FFFAFA', yellow: '#FFFF00', lightyellow: '#FFFFE0', ivory: '#FFFFF0', white: '#FFFFFF' }; + if (typeof color === 'string') { + return htmlColors[color]; + } + } + }, { + key: 'setColor', + + /** + * Set the color of the colorPicker + * Supported formats: + * 'red' --> HTML color string + * '#ffffff' --> hex string + * 'rbg(255,255,255)' --> rgb string + * 'rgba(255,255,255,1.0)' --> rgba string + * {r:255,g:255,b:255} --> rgb object + * {r:255,g:255,b:255,a:1.0} --> rgba object + * @param color + * @param setInitial + */ + value: function setColor(color) { + var setInitial = arguments[1] === undefined ? true : arguments[1]; + + if (color === 'none') { + return; + } + + var rgba = undefined; + + // if a html color shorthand is used, convert to hex + var htmlColor = this._isColorString(color); + if (htmlColor !== undefined) { + color = htmlColor; + } + + // check format + if (util.isString(color) === true) { + if (util.isValidRGB(color) === true) { + var rgbaArray = color.substr(4).substr(0, color.length - 5).split(','); + rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1 }; + } else if (util.isValidRGBA(color) === true) { + var rgbaArray = color.substr(5).substr(0, color.length - 6).split(','); + rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: rgbaArray[3] }; + } else if (util.isValidHex(color) === true) { + var rgbObj = util.hexToRGB(color); + rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1 }; + } + } else { + if (color instanceof Object) { + if (color.r !== undefined && color.g !== undefined && color.b !== undefined) { + var alpha = color.a !== undefined ? color.a : '1.0'; + rgba = { r: color.r, g: color.g, b: color.b, a: alpha }; + } + } + } + + // 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)); + } 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 + */ + value: function show(x, y) { + this.applied = false; + this.frame.style.display = 'block'; + this.frame.style.top = y + 'px'; + this.frame.style.left = x + 'px'; + this._generateHueCircle(); + } + }, { + key: '_hide', + + // ------------------------------------------ PRIVATE ----------------------------- // + + /** + * Hide the picker. Is called by the cancel button. + * Optional boolean to store the previous color for easy access later on. + * @param storePrevious + * @private + */ + value: function _hide() { + var storePrevious = arguments[0] === undefined ? true : arguments[0]; + + // store the previous color for next time; + if (storePrevious === true) { + this.previousColor = util.extend({}, this.color); + } + + if (this.applied === true) { + this.updateCallback(this.initialColor); + } + + this.frame.style.display = 'none'; + } + }, { + key: '_save', + + /** + * bound to the save button. Saves and hides. + * @private + */ + 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 + */ + value: function _apply() { + this.applied = true; + this.updateCallback(this.color); + this._updatePicker(this.color); + } + }, { + key: '_loadLast', + + /** + * load the color from the previous session. + * @private + */ + value: function _loadLast() { + if (this.previousColor !== undefined) { + this.setColor(this.previousColor, false); + } else { + alert('There is no last color to load...'); + } + } + }, { + key: '_setColor', + + /** + * set the color, place the picker + * @param rgba + * @param setInitial + * @private + */ + value: function _setColor(rgba) { + var setInitial = arguments[1] === undefined ? true : arguments[1]; + + // store the initial color + if (setInitial === true) { + this.initialColor = util.extend({}, rgba); + } + + this.color = rgba; + var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + + var angleConvert = 2 * Math.PI; + var radius = this.r * hsv.s; + var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h); + var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h); + + this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + 'px'; + this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + 'px'; + + this._updatePicker(rgba); + } + }, { + key: '_setOpacity', + + /** + * bound to opacity control + * @param value + * @private + */ + value: function _setOpacity(value) { + this.color.a = value / 100; + this._updatePicker(this.color); + } + }, { + key: '_setBrightness', + + /** + * bound to brightness control + * @param value + * @private + */ + value: function _setBrightness(value) { + var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); + hsv.v = value / 100; + var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); + rgba['a'] = this.color.a; + 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 + */ + value: function _updatePicker() { + var rgba = arguments[0] === undefined ? this.color : arguments[0]; + + var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + var ctx = this.colorPickerCanvas.getContext('2d'); + if (this.pixelRation === undefined) { + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); + } + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + + // clear the canvas + var w = this.colorPickerCanvas.clientWidth; + var h = this.colorPickerCanvas.clientHeight; + ctx.clearRect(0, 0, w, h); + + ctx.putImageData(this.hueCircle, 0, 0); + ctx.fillStyle = 'rgba(0,0,0,' + (1 - hsv.v) + ')'; + ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); + ctx.fill(); + + this.brightnessRange.value = 100 * hsv.v; + this.opacityRange.value = 100 * rgba.a; + + 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 + */ + value: function _setSize() { + this.colorPickerCanvas.style.width = '100%'; + this.colorPickerCanvas.style.height = '100%'; + + 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 + */ + value: function _create() { + this.frame = document.createElement('div'); + this.frame.className = 'vis-color-picker'; + + this.colorPickerDiv = document.createElement('div'); + this.colorPickerSelector = document.createElement('div'); + this.colorPickerSelector.className = 'vis-selector'; + this.colorPickerDiv.appendChild(this.colorPickerSelector); + + this.colorPickerCanvas = document.createElement('canvas'); + this.colorPickerDiv.appendChild(this.colorPickerCanvas); + + if (!this.colorPickerCanvas.getContext) { + var noCanvas = document.createElement('DIV'); + noCanvas.style.color = 'red'; + noCanvas.style.fontWeight = 'bold'; + noCanvas.style.padding = '10px'; + noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; + this.colorPickerCanvas.appendChild(noCanvas); + } else { + 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.colorPickerDiv.className = 'vis-color'; + + this.opacityDiv = document.createElement('div'); + this.opacityDiv.className = 'vis-opacity'; + + this.brightnessDiv = document.createElement('div'); + this.brightnessDiv.className = 'vis-brightness'; + + this.arrowDiv = document.createElement('div'); + this.arrowDiv.className = 'vis-arrow'; + + this.opacityRange = document.createElement('input'); + this.opacityRange.type = 'range'; + this.opacityRange.min = '0'; + this.opacityRange.max = '100'; + this.opacityRange.value = '100'; + this.opacityRange.className = 'vis-range'; + + this.brightnessRange = document.createElement('input'); + this.brightnessRange.type = 'range'; + this.brightnessRange.min = '0'; + this.brightnessRange.max = '100'; + this.brightnessRange.value = '100'; + this.brightnessRange.className = 'vis-range'; + + this.opacityDiv.appendChild(this.opacityRange); + this.brightnessDiv.appendChild(this.brightnessRange); + + var me = this; + this.opacityRange.onchange = function () { + me._setOpacity(this.value); + }; + this.opacityRange.oninput = function () { + me._setOpacity(this.value); + }; + this.brightnessRange.onchange = function () { + me._setBrightness(this.value); + }; + this.brightnessRange.oninput = function () { + me._setBrightness(this.value); + }; + + 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.innerHTML = 'opacity:'; + + 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.innerHTML = 'initial'; + + 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.innerHTML = 'apply'; + this.applyButton.onclick = this._apply.bind(this); + + 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.innerHTML = 'load last'; + this.loadButton.onclick = this._loadLast.bind(this); + + this.frame.appendChild(this.colorPickerDiv); + this.frame.appendChild(this.arrowDiv); + this.frame.appendChild(this.brightnessLabel); + this.frame.appendChild(this.brightnessDiv); + this.frame.appendChild(this.opacityLabel); + this.frame.appendChild(this.opacityDiv); + this.frame.appendChild(this.newColorDiv); + this.frame.appendChild(this.initialColorDiv); + + this.frame.appendChild(this.cancelButton); + this.frame.appendChild(this.applyButton); + this.frame.appendChild(this.saveButton); + this.frame.appendChild(this.loadButton); + } + }, { + key: '_bindHammer', + + /** + * bind hammer to the color picker + * @private + */ + value: function _bindHammer() { + var _this = this; + + this.drag = {}; + this.pinch = {}; + this.hammer = new Hammer(this.colorPickerCanvas); + this.hammer.get('pinch').set({ enable: true }); + + hammerUtil.onTouch(this.hammer, function (event) { + _this._moveSelector(event); + }); + this.hammer.on('tap', function (event) { + _this._moveSelector(event); + }); + this.hammer.on('panstart', function (event) { + _this._moveSelector(event); + }); + this.hammer.on('panmove', function (event) { + _this._moveSelector(event); + }); + this.hammer.on('panend', function (event) { + _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 + */ + value: function _generateHueCircle() { + if (this.generated === false) { + var ctx = this.colorPickerCanvas.getContext('2d'); + if (this.pixelRation === undefined) { + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); + } + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + + // clear the canvas + var w = this.colorPickerCanvas.clientWidth; + var h = this.colorPickerCanvas.clientHeight; + ctx.clearRect(0, 0, w, h); + + // draw hue circle + var x = undefined, + y = undefined, + hue = undefined, + sat = undefined; + this.centerCoordinates = { x: w * 0.5, y: h * 0.5 }; + this.r = 0.49 * w; + var angleConvert = 2 * Math.PI / 360; + var hfac = 1 / 360; + var sfac = 1 / this.r; + var rgb = undefined; + for (hue = 0; hue < 360; hue++) { + for (sat = 0; sat < this.r; sat++) { + x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue); + y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue); + rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1); + ctx.fillStyle = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')'; + ctx.fillRect(x - 0.5, y - 0.5, 2, 2); + } + } + ctx.strokeStyle = 'rgba(0,0,0,1)'; + ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); + ctx.stroke(); + + this.hueCircle = ctx.getImageData(0, 0, w, h); + } + this.generated = true; + } + }, { + key: '_moveSelector', + + /** + * move the selector. This is called by hammer functions. + * + * @param event + * @private + */ + value: function _moveSelector(event) { + var rect = this.colorPickerDiv.getBoundingClientRect(); + var left = event.center.x - rect.left; + var top = event.center.y - rect.top; + + var centerY = 0.5 * this.colorPickerDiv.clientHeight; + var centerX = 0.5 * this.colorPickerDiv.clientWidth; + + var x = left - centerX; + var y = top - centerY; + + var angle = Math.atan2(x, y); + var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX); + + var newTop = Math.cos(angle) * radius + centerY; + var newLeft = Math.sin(angle) * radius + centerX; + + this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + 'px'; + this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + 'px'; + + // set color + var h = angle / (2 * Math.PI); + h = h < 0 ? h + 1 : h; + var s = radius / this.r; + var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); + hsv.h = h; + hsv.s = s; + var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); + rgba['a'] = this.color.a; + this.color = rgba; + + // update previews + 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 + ')'; + } + }]); + + return ColorPicker; + })(); + + exports['default'] = ColorPicker; + module.exports = exports['default']; + /***/ } /******/ ]) }); diff --git a/docs/network/configure.html b/docs/network/configure.html index 09284e5d..63d8eff5 100644 --- a/docs/network/configure.html +++ b/docs/network/configure.html @@ -100,7 +100,8 @@ var options = { configure: { enabled: true, filter: 'nodes,edges', - container: undefined + container: undefined, + showButton: true } } @@ -154,6 +155,12 @@ function (option, path) { undefined This allows you to put the configure list in another HTML container than below the network. + + showButton + Boolean + true + Show the generate options button at the bottom of the configurator. + diff --git a/docs/network/index.html b/docs/network/index.html index 1ea95686..a00277f8 100644 --- a/docs/network/index.html +++ b/docs/network/index.html @@ -466,6 +466,33 @@ var locales = { container with the module name to contain its options. + + on(String event name, Function callback) + + + + Returns: none + Set an event listener. Depending on the type of event you get different parameters for the callback function. Look at the event section of the documentation for more information. + + + + off(String event name, Function callback) + + + + Returns: none + Remove an event listener. The function you supply has to be the exact same as the one you used in the on function. If no function is supplied, all listeners will be removed. Look at the event section of the documentation for more information. + + + + once(String event name, Function callback) + + + + Returns: none + Set an event listener only once. After it has taken place, the event listener will be removed. Depending on the type of event you get different parameters for the callback function. Look at the event section of the documentation for more information. + + @@ -914,11 +941,11 @@ var locales = { - getPosition() + getViewPosition() Returns: Number - Returns the current central focus point of the camera. + Returns the current central focus point of the view. @@ -1255,8 +1282,8 @@ var options = { zoom - {direction:'+'/'-'} - Fired when the user zooms in or out. The properties tell you which direction the zoom is in. + {direction:'+'/'-', scale: Number} + Fired when the user zooms in or out. The properties tell you which direction the zoom is in. The scale is a number greater than 0, which is the same that you get with network.getScale(). showPopup diff --git a/docs/network/physics.html b/docs/network/physics.html index d162aaba..b5d6cc39 100644 --- a/docs/network/physics.html +++ b/docs/network/physics.html @@ -184,7 +184,7 @@ network.setOptions(options); stabilization Object | BooleanObject When true, the network is stabilized on load using default settings. If false, stabilization is disabled. To further customize this, you can supply an object. stabilization.enabled Boolean true Toggle the stabilization. This is an optional property. If undefined, it is automatically set to true when any of the properties of this object are defined. stabilization.iterations Number 1000 The physics module tries to stabilize the network on load up til a maximum number of iterations defined here. If the network stabilized with less, you are finished before the maximum number. - stabilization.updateInterval Number 100 When stabilizing, the DOM can freeze. You can chop the stabilization up into pieces to show a loading bar for instance. The interval determines after how many iterations the stabilizationProgress event is triggered. + stabilization.updateInterval Number 50 When stabilizing, the DOM can freeze. You can chop the stabilization up into pieces to show a loading bar for instance. The interval determines after how many iterations the stabilizationProgress event is triggered. stabilization.onlyDynamicEdges Boolean false If you have predefined the position of all nodes and only want to stabilize the dynamic smooth edges, set this to true. It freezes all nodes except the invisible dynamic smooth curve support nodes. If you want the visible nodes to move and stabilize, do not use this. 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. diff --git a/examples/network/categories/02_random_nodes.html b/examples/network/categories/02_random_nodes.html deleted file mode 100644 index b6ebe89b..00000000 --- a/examples/network/categories/02_random_nodes.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - Network | Random nodes - - - - - - - - - - -

- Generate a random network with nodes and edges. -

-

- - - -

- -
- - - diff --git a/examples/network/categories/07_selections.html b/examples/network/categories/07_selections.html deleted file mode 100644 index 9f74849b..00000000 --- a/examples/network/categories/07_selections.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - Network | Selections - - - - - - - - - -

- Select nodes or edges by clicking them. To select multiple nodes, long-press (hold) them. -

-
-

-
-
-
-
-
diff --git a/examples/network/categories/11_styling.html b/examples/network/categories/11_styling.html
deleted file mode 100644
index adcb7a10..00000000
--- a/examples/network/categories/11_styling.html
+++ /dev/null
@@ -1,145 +0,0 @@
-
-
-
-  Network | Custom style
-
-  
-
-  
-  
-
-  
-  
-
-
-

- Custom styles for nodes can be applied globally, per group, of to individual nodes. Same holds for edges. -

- -
- - diff --git a/examples/network/categories/12_arrows.html b/examples/network/categories/12_arrows.html deleted file mode 100644 index 9e43243e..00000000 --- a/examples/network/categories/12_arrows.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - Network | Arrows - - - - - - - - - - - -

- This example shows the different options for arrows. -

- -
- - diff --git a/examples/network/categories/13_dashed_lines.html b/examples/network/categories/13_dashed_lines.html deleted file mode 100644 index 87446e58..00000000 --- a/examples/network/categories/13_dashed_lines.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - Network | Dashed lines - - - - - - - - - - - -

- This example shows the different options for dashed lines. -

- -
- - diff --git a/examples/network/categories/18_fully_random_nodes_clustering.html b/examples/network/categories/18_fully_random_nodes_clustering.html deleted file mode 100644 index 4f1e565e..00000000 --- a/examples/network/categories/18_fully_random_nodes_clustering.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - Network | Fully random nodes clustering - - - - - - - - - - - -

Clustering - Fully random network

-
- This example shows a fully randomly generated set of nodes and connected edges. - By clicking the checkbox you can turn clustering on and off. If you increase the number of nodes to - a value higher than 100, automatic clustering is used before the initial draw (assuming the checkbox is checked). -
-
- Clustering is done automatically when zooming out. When zooming in over the cluster, the cluster pops open. When the cluster is very big, a special instance - will be created and the cluster contents will only be simulated in there. Double click will also open a cluster. -
-
- Try values of 500 and 5000 with and without clustering. All thresholds can be changed to suit your dataset. -
-
-
- - - - - -
-
- -
- -

- - diff --git a/examples/network/categories/19_scale_free_graph_clustering.html b/examples/network/categories/19_scale_free_graph_clustering.html deleted file mode 100644 index c68b13db..00000000 --- a/examples/network/categories/19_scale_free_graph_clustering.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - Network | Scale free network clustering - - - - - - - - - - - -

Clustering - Scale-Free-Network

-
- This example shows the randomly generated scale-free-network set of nodes and connected edges from example 2. - By clicking the checkbox you can turn clustering on and off. If you increase the number of nodes to - a value higher than 100, automatic clustering is used before the initial draw (assuming the checkbox is checked). -
-
- Clustering is done automatically when zooming out. When zooming in over the cluster, the cluster pops open. When the cluster is very big, a special instance - will be created and the cluster contents will only be simulated in there. Double click will also open a cluster. -
-
- Try values of 500 and 5000 with and without clustering. All thresholds can be changed to suit your dataset. - Experiment with the clusterEdgeThreshold, which increases the formation of clusters when zoomed out (assuming the checkbox is checked). -
-
-
- - - - - - - -
-
- -
- -

- - diff --git a/examples/network/categories/22_les_miserables.html b/examples/network/categories/22_les_miserables.html deleted file mode 100644 index cb650b65..00000000 --- a/examples/network/categories/22_les_miserables.html +++ /dev/null @@ -1,386 +0,0 @@ - - - - Network | Les miserables - - - - - - - - - - - -

- Relations between the characters of "Les miserables". -

-
- - diff --git a/examples/network/categories/26_staticSmoothCurves.html b/examples/network/categories/26_staticSmoothCurves.html deleted file mode 100644 index 9854a61d..00000000 --- a/examples/network/categories/26_staticSmoothCurves.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - Network | Static smooth curves - - - - - - - - - - -

Static smooth curves

-
- All the smooth curves in the examples so far have been using dynamic smooth curves. This means that each curve has a - support node which takes part in the physics simulation. For large networks or dense clusters, this may not be the ideal - solution. To solve this, static smooth curves have been added. The static smooth curves are based only on the positions of the connected - nodes. There are multiple ways to determine the way this curve is drawn. This example shows the effect of the different - types.

- Drag the nodes around each other to see how the smooth curves are drawn for each setting. For animated system, we - recommend only the continuous mode. In the next example you can see the effect of these methods on a large network. Keep in mind - that the direction (the from and to) of the curve matters. -

-
- -

- Smooth curve type: - -

-

- Roundness (0..1): (0.5 is max roundness for continuous, 1.0 for the others) -

- -
- - - - - diff --git a/examples/network/categories/27_world_cup_network.html b/examples/network/categories/27_world_cup_network.html deleted file mode 100644 index 8d9f9551..00000000 --- a/examples/network/categories/27_world_cup_network.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - Network | Static smooth curves - World Cup Network - - - - - - - - - - - - -

Static smooth curves - World Cup Network

- -
- The static smooth curves are based only on the positions of the connected - nodes. - There are multiple ways to determine the way this curve is drawn. - This example shows the effect of the different types on a large network. -

- Also shown in this example is the inheritColor option of the edges as well as - the roundness factor.
-

- To improve performance, the physics have been disabled with: -
{barnesHut: {gravitationalConstant: 0, centralGravity: 0, - springConstant: 0}}
and we have enabled - the toggle hideEdgesOnDrag. -

-
- -Smooth curve type: -
-inheritColor option: -
-Roundness (0..1): (0.5 is max roundness for continuous, 1.0 -for the others) -
-Hide edges on drag:
-Hide nodes on drag: - -
- - - - - diff --git a/examples/network/categories/29_neighbourhood_highlight.html b/examples/network/categories/29_neighbourhood_highlight.html deleted file mode 100644 index 500f3e73..00000000 --- a/examples/network/categories/29_neighbourhood_highlight.html +++ /dev/null @@ -1,222 +0,0 @@ - - - - Network | Static smooth curves - World Cup Network - - - - - - - - - - - - -

Dynamic Data - Neighbourhood Highlight

-
- This example shows the power of the DataSet. Once a node is clicked, all nodes are greyed out except for the first and second order connected nodes. - In this example we show how you can determine the order of connection per node as well as applying individual styling to the nodes based on whether or not - they are connected to the selected node. The code doing the highlighting only takes about 20ms, the rest of the time is the redrawing of the network (9200 edges..). -

-
- -
- - - - diff --git a/examples/network/categories/01_basic_usage.html b/examples/network/categories/basic_usage.html similarity index 83% rename from examples/network/categories/01_basic_usage.html rename to examples/network/categories/basic_usage.html index 278b938e..0df9be35 100644 --- a/examples/network/categories/01_basic_usage.html +++ b/examples/network/categories/basic_usage.html @@ -3,8 +3,8 @@ Network | Basic usage - - + + + + + + + +

Static smooth curves - World Cup Network

+ +
+ The static smooth curves are based only on the positions of the connected + nodes. + There are multiple ways to determine the way this curve is drawn. + This example shows the effect of the different types on a large network. +

+ Also shown in this example is the inheritColor option of the edges as well as + the roundness factor. Because the physics has been disabled, the dynamic smooth curves do not work here. +
+ +
+ +
+ + + + + diff --git a/examples/network/categories/events/clickEvents.html b/examples/network/categories/events/interactionEvents.html similarity index 98% rename from examples/network/categories/events/clickEvents.html rename to examples/network/categories/events/interactionEvents.html index 3b276059..a1030f15 100644 --- a/examples/network/categories/events/clickEvents.html +++ b/examples/network/categories/events/interactionEvents.html @@ -1,7 +1,7 @@ - Network | Basic usage + Network | Interaction events diff --git a/examples/network/categories/exampleApplications/les_miserables.html b/examples/network/categories/exampleApplications/les_miserables.html new file mode 100644 index 00000000..b8eca6a7 --- /dev/null +++ b/examples/network/categories/exampleApplications/les_miserables.html @@ -0,0 +1,396 @@ + + + + Network | Les miserables + + + + + + + + + + + +

+ Relations between the characters of "Les miserables". +

+ +
+ + diff --git a/examples/network/categories/exampleApplications/loadingBar.html b/examples/network/categories/exampleApplications/loadingBar.html new file mode 100644 index 00000000..12f7002c --- /dev/null +++ b/examples/network/categories/exampleApplications/loadingBar.html @@ -0,0 +1,504 @@ + + + + Network | Les miserables + + + + + + + + + + + +

+ With the new stabilization events you can implement your own custom loading bar for all those long loading times! +

+ +
+
+
+
+
0%
+
+
+
+
+
+
+ + diff --git a/examples/network/categories/exampleApplications/neighbourhood_highlight.html b/examples/network/categories/exampleApplications/neighbourhood_highlight.html new file mode 100644 index 00000000..6b41ccb7 --- /dev/null +++ b/examples/network/categories/exampleApplications/neighbourhood_highlight.html @@ -0,0 +1,162 @@ + + + + Network | Static smooth curves - World Cup Network + + + + + + + + + + + + +

Dynamic Data - Neighbourhood Highlight

+
+ This example shows the power of the DataSet. Once a node is clicked, all nodes are greyed out except for the first and second order connected nodes. + In this example we show how you can determine the order of connection per node as well as applying individual styling to the nodes based on whether or not + they are connected to the selected node. The code doing the highlighting only takes about 1ms, the rest of the time is the redrawing of the network (9200 edges..). +

+
+ +
+ + + + diff --git a/examples/network/categories/17_network_info.html b/examples/network/categories/exampleApplications/nodeLegend.html similarity index 91% rename from examples/network/categories/17_network_info.html rename to examples/network/categories/exampleApplications/nodeLegend.html index c40c8a1f..b7fe1230 100644 --- a/examples/network/categories/17_network_info.html +++ b/examples/network/categories/exampleApplications/nodeLegend.html @@ -18,8 +18,8 @@ } - - + + - + -
+
diff --git a/examples/network/categories/28_world_cup_network_performance.html b/examples/network/categories/exampleApplications/worldCupPerformance.html similarity index 88% rename from examples/network/categories/28_world_cup_network_performance.html rename to examples/network/categories/exampleApplications/worldCupPerformance.html index d0d98f3c..2b700e36 100644 --- a/examples/network/categories/28_world_cup_network_performance.html +++ b/examples/network/categories/exampleApplications/worldCupPerformance.html @@ -5,10 +5,10 @@ Network | Static smooth curves - World Cup Network - - + + - + - + @@ -43,8 +43,7 @@ function redrawAll() { - network = null; - + // remove positoins for (var i = 0; i < nodes.length; i++) { delete nodes[i].x; delete nodes[i].y; diff --git a/examples/network/categories/nodeStyles/colors.html b/examples/network/categories/nodeStyles/colors.html index 0868b650..5a498957 100644 --- a/examples/network/categories/nodeStyles/colors.html +++ b/examples/network/categories/nodeStyles/colors.html @@ -55,7 +55,10 @@ nodes: nodes, edges: edges }; - var options = {nodes:{borderWidth:2},interaction:{hover:true}}; + var options = { + nodes: {borderWidth: 2}, + interaction: {hover: true} + } var network = new vis.Network(container, data, options); diff --git a/examples/network/categories/nodeStyles/shadows.html b/examples/network/categories/nodeStyles/shadows.html new file mode 100644 index 00000000..8674ebaa --- /dev/null +++ b/examples/network/categories/nodeStyles/shadows.html @@ -0,0 +1,119 @@ + + + + Network | Groups + + + + + + + + + + + +
+ + + diff --git a/examples/network/categories/physics/25_physics_configuration.html b/examples/network/categories/physics/25_physics_configuration.html deleted file mode 100644 index 3277a38e..00000000 --- a/examples/network/categories/physics/25_physics_configuration.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - Network | Playing with Physics - - - - - - - - - - - - -

Playing with Physics

-
- Every dataset is different. Nodes can have different sizes based on content, interconnectivity can be high or low etc. Because of this, network has a special option - that the user can use to explore which settings may be good for him or her. This is ment to be used during the development phase when you are implementing vis.js. Once you have found - settings you are happy with, you can supply them to network using the documented physics options. - - On start, the default settings will be loaded. Keep in mind that selecting the hierarchical simulation mode disables smooth curves. These will not be enabled again afterwards. -
-
-
- -

- - diff --git a/examples/network/categories/physics/physicsConfiguration.html b/examples/network/categories/physics/physicsConfiguration.html new file mode 100644 index 00000000..fcd8e5ef --- /dev/null +++ b/examples/network/categories/physics/physicsConfiguration.html @@ -0,0 +1,80 @@ + + + + Network | Playing with Physics + + + + + + + + + + + + + + +

Playing with Physics

+
+ Every dataset is different. Nodes can have different sizes based on content, interconnectivity can be high or low etc. Because of this, the network configurator can be used + to explore which settings may be good for him or her. This is ment to be used during the development phase when you are implementing vis.js. Once you have found + settings you are happy with, you can supply them to network using the documented physics options. +
+
+
+
+ +

+ + diff --git a/examples/network/categories/rest/clustering.html b/examples/network/categories/rest/clustering.html index 0e304245..7c81f4a5 100644 --- a/examples/network/categories/rest/clustering.html +++ b/examples/network/categories/rest/clustering.html @@ -42,12 +42,12 @@ Click any of the buttons below to cluster the network. On every push the network // create an array with nodes var nodes = [ {id: 1, label: 'Node 1', color:'orange'}, - {id: 2, label: 'Node 2', color:'DarkViolet'}, + {id: 2, label: 'Node 2', color:'DarkViolet', font:{color:'white'}}, {id: 3, label: 'Node 3', color:'orange'}, - {id: 4, label: 'Node 4', color:'DarkViolet'}, + {id: 4, label: 'Node 4', color:'DarkViolet', font:{color:'white'}}, {id: 5, label: 'Node 5', color:'orange'}, {id: 6, label: 'cid = 1', cid:1, color:'orange'}, - {id: 7, label: 'cid = 1', cid:1, color:'DarkViolet'}, + {id: 7, label: 'cid = 1', cid:1, color:'DarkViolet', font:{color:'white'}}, {id: 8, label: 'cid = 1', cid:1, color:'lime'}, {id: 9, label: 'cid = 1', cid:1, color:'orange'}, {id: 10, label: 'cid = 1', cid:1, color:'lime'} @@ -72,7 +72,7 @@ Click any of the buttons below to cluster the network. On every push the network nodes: nodes, edges: edges }; - var options = {layout:{randomSeed:8},nodes:{font:{strokeWidth:5}}}; + var options = {layout:{randomSeed:8}}; var network = new vis.Network(container, data, options); network.on("selectNode", function(params) { if (params.nodes.length == 1) { diff --git a/examples/network/categories/rest/clusteringByZoom.html b/examples/network/categories/rest/clusteringByZoom.html new file mode 100644 index 00000000..b08029da --- /dev/null +++ b/examples/network/categories/rest/clusteringByZoom.html @@ -0,0 +1,156 @@ + + + + Network | Clustering + + + + + + + + + + + +

+ You can zoom in and out to cluster/decluster. +

+Stabilize when clustering: +
+ + + + + diff --git a/examples/network/categories/rest/configuration.html b/examples/network/categories/rest/configuration.html new file mode 100644 index 00000000..bdc00837 --- /dev/null +++ b/examples/network/categories/rest/configuration.html @@ -0,0 +1,72 @@ + + + + Network | Playing with Physics + + + + + + + + + + + + + + +

+ The configurator can be used to play with the options. In this example, all options that can be configured with this tool are shown. + You can also supply a custom filter function or filter string. You can press the generate options button below to have an options object printed. You can then use + this in the network. +

+
+
+ +

+ + diff --git a/examples/network/categories/rest/performance.html b/examples/network/categories/rest/performance.html new file mode 100644 index 00000000..2f53e6b1 --- /dev/null +++ b/examples/network/categories/rest/performance.html @@ -0,0 +1,91 @@ + + + + Network | Random nodes + + + + + + + + + + + +

+ Generate a random network with nodes and edges. +

+

+

+ + + +
+

+ +
+ + + diff --git a/examples/network/data/WorldCup2014.js b/examples/network/data/WorldCup2014.js index 5c36f83e..f08a4a41 100644 --- a/examples/network/data/WorldCup2014.js +++ b/examples/network/data/WorldCup2014.js @@ -9968,8 +9968,3 @@ var edges = [ {from: 732, to: 733}, {from: 734, to: 735} ]; - -var data = { - nodes: nodes, - edges: edges -}; diff --git a/lib/network/Network.js b/lib/network/Network.js index 00adb0e6..f0bae773 100644 --- a/lib/network/Network.js +++ b/lib/network/Network.js @@ -464,7 +464,7 @@ Network.prototype.selectEdges = function() {return this.selectionHandler Network.prototype.unselectAll = function() {return this.selectionHandler.unselectAll.apply(this.selectionHandler,arguments);}; Network.prototype.redraw = function() {return this.renderer.redraw.apply(this.renderer,arguments);}; Network.prototype.getScale = function() {return this.view.getScale.apply(this.view,arguments);}; -Network.prototype.getPosition = function() {return this.view.getPosition.apply(this.view,arguments);}; +Network.prototype.getViewPosition = function() {return this.view.getViewPosition.apply(this.view,arguments);}; Network.prototype.fit = function() {return this.view.fit.apply(this.view,arguments);}; Network.prototype.moveTo = function() {return this.view.moveTo.apply(this.view,arguments);}; Network.prototype.focus = function() {return this.view.focus.apply(this.view,arguments);}; diff --git a/lib/network/modules/Clustering.js b/lib/network/modules/Clustering.js index bc52d8ef..e68234cc 100644 --- a/lib/network/modules/Clustering.js +++ b/lib/network/modules/Clustering.js @@ -9,6 +9,8 @@ class ClusterEngine { this.options = {}; this.defaultOptions = {}; util.extend(this.options, this.defaultOptions); + + this.body.emitter.on('_resetData', () => {this.clusteredNodes = {};}) } setOptions(options) { @@ -94,29 +96,42 @@ class ClusterEngine { let childNodesObj = {}; let childEdgesObj = {}; let nodeId = this.body.nodeIndices[i]; - if (this.body.nodes[nodeId].edges.length === 1) { + let visibleEdges = 0; + let edge; + for (let j = 0; j < this.body.nodes[nodeId].edges.length; j++) { + if (this.body.nodes[nodeId].edges[j].options.hidden === false) { + visibleEdges++; + edge = this.body.nodes[nodeId].edges[j]; + } + } + + if (visibleEdges === 1) { // this is an outlier - let edge = this.body.nodes[nodeId].edges[0]; let childNodeId = this._getConnectedId(edge, nodeId); if (childNodeId !== nodeId) { if (options.joinCondition === undefined) { - childEdgesObj[edge.id] = edge; - childNodesObj[nodeId] = this.body.nodes[nodeId]; - childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + if (this._checkIfUsed(clusters,nodeId,edge.id) === false && this._checkIfUsed(clusters,childNodeId,edge.id) === false) { + childEdgesObj[edge.id] = edge; + childNodesObj[nodeId] = this.body.nodes[nodeId]; + childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + } } else { let clonedOptions = this._cloneOptions(this.body.nodes[nodeId]); - if (options.joinCondition(clonedOptions) === true) { + if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters,nodeId,edge.id) === false) { childEdgesObj[edge.id] = edge; childNodesObj[nodeId] = this.body.nodes[nodeId]; } clonedOptions = this._cloneOptions(this.body.nodes[childNodeId]); - if (options.joinCondition(clonedOptions) === true) { + if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters,nodeId,edge.id) === false) { childEdgesObj[edge.id] = edge; childNodesObj[childNodeId] = this.body.nodes[childNodeId]; } } - clusters.push({nodes:childNodesObj, edges:childEdgesObj}) + + if (Object.keys(childNodesObj).length > 0 && Object.keys(childEdgesObj).length > 0) { + clusters.push({nodes: childNodesObj, edges: childEdgesObj}) + } } } } @@ -130,6 +145,17 @@ class ClusterEngine { } } + + _checkIfUsed(clusters, nodeId, edgeId) { + for (let i = 0; i < clusters.length; i++) { + let cluster = clusters[i]; + if (cluster.nodes[nodeId] !== undefined || cluster.edges[edgeId] !== undefined) { + return true; + } + } + return false; + } + /** * suck all connected nodes of a node into the node. * @param nodeId @@ -283,10 +309,6 @@ class ClusterEngine { let clusterNodeProperties = util.deepExtend({},options.clusterNodeProperties); - // check if we have an unique id; - if (clusterNodeProperties.id === undefined) {clusterNodeProperties.id = 'cluster:' + util.randomUUID();} - let clusterId = clusterNodeProperties.id; - // construct the clusterNodeProperties if (options.processProperties !== undefined) { // get the childNode options @@ -309,6 +331,10 @@ class ClusterEngine { } } + // check if we have an unique id; + if (clusterNodeProperties.id === undefined) {clusterNodeProperties.id = 'cluster:' + util.randomUUID();} + let clusterId = clusterNodeProperties.id; + if (clusterNodeProperties.label === undefined) { clusterNodeProperties.label = 'cluster'; } @@ -472,7 +498,8 @@ class ClusterEngine { delete this.body.edges[edgeId]; } else { - // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster and make a new temporary edge. + + // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster so it will be released when that cluster is opened. if (this.clusteredNodes[edge.fromId] !== undefined || this.clusteredNodes[edge.toId] !== undefined) { let fromId, toId; let clusteredNode = this.clusteredNodes[edge.fromId] || this.clusteredNodes[edge.toId]; @@ -480,23 +507,26 @@ class ClusterEngine { let clusterNode = this.body.nodes[clusterId]; clusterNode.containedEdges[edgeId] = edge; - if (this.clusteredNodes[edge.fromId] !== undefined) { - fromId = clusterId; - toId = edge.toId; + // if both from and to nodes are visible, we create a new temporary edge + if (edge.from.options.hidden !== true && edge.to.options.hidden !== true) { + if (this.clusteredNodes[edge.fromId] !== undefined) { + fromId = clusterId; + toId = edge.toId; + } + else { + fromId = edge.fromId; + toId = clusterId; + } + + let clonedOptions = this._cloneOptions(edge, 'edge'); + let id = 'clusterEdge:' + util.randomUUID(); + util.deepExtend(clonedOptions, clusterNode.clusterEdgeProperties); + util.deepExtend(clonedOptions, {from: fromId, to: toId, hidden: false, physics: true, id: id}); + let newEdge = this.body.functions.createEdge(clonedOptions); + + this.body.edges[id] = newEdge; + this.body.edges[id].connect(); } - else { - fromId = edge.fromId; - toId = clusterId; - } - - let clonedOptions = this._cloneOptions(edge, 'edge'); - let id = 'clusterEdge:' + util.randomUUID(); - util.deepExtend(clonedOptions, clusterNode.clusterEdgeProperties); - util.deepExtend(clonedOptions, {from:fromId, to:toId, hidden:false, physics:true, id: id}); - let newEdge = this.body.functions.createEdge(clonedOptions); - - this.body.edges[id] = newEdge; - this.body.edges[id].connect(); } else { edge.options.hidden = false; diff --git a/lib/network/modules/EdgesHandler.js b/lib/network/modules/EdgesHandler.js index e63cadaa..7b63687d 100644 --- a/lib/network/modules/EdgesHandler.js +++ b/lib/network/modules/EdgesHandler.js @@ -69,7 +69,7 @@ class EdgesHandler { } } }, - selectionWidth: 1, + selectionWidth: 1.5, selfReferenceSize:20, shadow:{ enabled: false, diff --git a/lib/network/modules/InteractionHandler.js b/lib/network/modules/InteractionHandler.js index bf610b14..f79ae7c5 100644 --- a/lib/network/modules/InteractionHandler.js +++ b/lib/network/modules/InteractionHandler.js @@ -407,10 +407,10 @@ class InteractionHandler { this.body.emitter.emit('_requestRedraw'); if (scaleOld < scale) { - this.body.emitter.emit('zoom', {direction: '+'}); + this.body.emitter.emit('zoom', {direction: '+', scale: this.body.view.scale}); } else { - this.body.emitter.emit('zoom', {direction: '-'}); + this.body.emitter.emit('zoom', {direction: '-', scale: this.body.view.scale}); } } } diff --git a/lib/network/modules/NodesHandler.js b/lib/network/modules/NodesHandler.js index 87d62170..17459a57 100644 --- a/lib/network/modules/NodesHandler.js +++ b/lib/network/modules/NodesHandler.js @@ -24,7 +24,7 @@ class NodesHandler { this.options = {}; this.defaultOptions = { borderWidth: 1, - borderWidthSelected: undefined, + borderWidthSelected: 2, brokenImage:undefined, color: { border: '#2B7CE9', @@ -378,13 +378,13 @@ class NodesHandler { let nodeObj = {}; // used to quickly check if node already exists for (let i = 0; i < node.edges.length; i++) { let edge = node.edges[i]; - if (edge.toId === nodeId) { + if (edge.toId == nodeId) { // these are double equals since ids can be numeric or string if (nodeObj[edge.fromId] === undefined) { nodeList.push(edge.fromId); nodeObj[edge.fromId] = true; } } - else if (edge.fromId === nodeId) { + else if (edge.fromId == nodeId) { // these are double equals since ids can be numeric or string if (nodeObj[edge.toId] === undefined) { nodeList.push(edge.toId); nodeObj[edge.toId] = true; diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index c3782497..5ebef71d 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -69,7 +69,7 @@ class PhysicsEngine { stabilization: { enabled: true, iterations: 1000, // maximum number of iteration to stabilize - updateInterval: 100, + updateInterval: 50, onlyDynamicEdges: false, fit: true }, diff --git a/lib/network/modules/View.js b/lib/network/modules/View.js index 68464b40..f7f301c2 100644 --- a/lib/network/modules/View.js +++ b/lib/network/modules/View.js @@ -325,7 +325,7 @@ class View { return this.body.view.scale; } - getPosition() { + getViewPosition() { return {x:this.body.view.translation.x, y:this.body.view.translation.y}; } diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index 6d7fd840..24060c27 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -118,13 +118,13 @@ class Node { throw "Node must have an id"; } + + // set these options locally if (options.x !== undefined) {this.x = parseInt(options.x); this.predefinedPosition = true;} if (options.y !== undefined) {this.y = parseInt(options.y); this.predefinedPosition = true;} if (options.size !== undefined) {this.baseSize = options.size;} if (options.value !== undefined) {options.value = parseInt(options.value);} - // this transforms all shorthands into fully defined options - Node.parseOptions(this.options, options, true); // copy group options if (typeof options.group === 'number' || (typeof options.group === 'string' && options.group != '')) { @@ -134,6 +134,9 @@ class Node { this.options.color = util.parseColor(this.options.color); } + // this transforms all shorthands into fully defined options + Node.parseOptions(this.options, options, true); + // load the images if (this.options.image !== undefined && this.options.image != "") { if (this.imagelist) { diff --git a/lib/network/options.js b/lib/network/options.js index e1ae5bc1..5dddbf06 100644 --- a/lib/network/options.js +++ b/lib/network/options.js @@ -21,6 +21,7 @@ let allOptions = { enabled: {boolean}, filter: {boolean,string,array,fn}, container: {dom}, + showButton: {boolean}, __type__: {object,boolean,string,array,fn} }, edges: { @@ -322,11 +323,11 @@ let configureOptions = { min: [10, 0, 200, 1], max: [30, 0, 200, 1], label: { - enabled: true, + enabled: false, min: [14, 0, 200, 1], max: [30, 0, 200, 1], maxVisible: [30, 0, 200, 1], - drawThreshold: [3, 0, 20, 1] + drawThreshold: [5, 0, 20, 1] } }, shadow:{ @@ -357,12 +358,12 @@ let configureOptions = { size: [14, 0, 100, 1], // px face: ['arial', 'verdana', 'tahoma'], background: ['color','none'], - strokeWidth: [1, 0, 50, 1], // px + strokeWidth: [2, 0, 50, 1], // px strokeColor: ['color','#ffffff'], align: ['horizontal', 'top', 'middle', 'bottom'] }, hidden: false, - hoverWidth: [2, 0, 5, 0.1], + hoverWidth: [1.5, 0, 5, 0.1], physics: true, scaling: { min: [1, 0, 100, 1], @@ -372,7 +373,7 @@ let configureOptions = { min: [14, 0, 200, 1], max: [30, 0, 200, 1], maxVisible: [30, 0, 200, 1], - drawThreshold: [3, 0, 20, 1] + drawThreshold: [5, 0, 20, 1] } }, selectionWidth: [1.5, 0, 5, 0.1], @@ -391,7 +392,7 @@ let configureOptions = { width: [1, 0, 30, 1] }, layout: { - randomSeed: [0, 0, 500, 1], + //randomSeed: [0, 0, 500, 1], hierarchical: { enabled: false, levelSeparation: [150, 20, 500, 5], @@ -427,37 +428,37 @@ let configureOptions = { gravitationalConstant: [-2000, -30000, 0, 50], centralGravity: [0.3, 0, 10, 0.05], springLength: [95, 0, 500, 5], - springConstant: [0.04, 0, 5, 0.005], + springConstant: [0.04, 0, 1.2, 0.005], damping: [0.09, 0, 1, 0.01], avoidOverlap: [0, 0, 1, 0.01] }, forceAtlas2Based: { //theta: [0.5, 0.1, 1, 0.05], - gravitationalConstant: [-800, -3000, 0, 1], + gravitationalConstant: [-50, -500, 0, 1], centralGravity: [0.01, 0, 1, 0.005], springLength: [95, 0, 500, 5], - springConstant: [0.08, 0, 5, 0.005], + springConstant: [0.08, 0, 1.2, 0.005], damping: [0.4, 0, 1, 0.01], avoidOverlap: [0, 0, 1, 0.01] }, repulsion: { centralGravity: [0.2, 0, 10, 0.05], springLength: [200, 0, 500, 5], - springConstant: [0.05, 0, 5, 0.005], + springConstant: [0.05, 0, 1.2, 0.005], nodeDistance: [100, 0, 500, 5], damping: [0.09, 0, 1, 0.01] }, hierarchicalRepulsion: { centralGravity: [0.2, 0, 10, 0.05], springLength: [100, 0, 500, 5], - springConstant: [0.01, 0, 5, 0.005], + springConstant: [0.01, 0, 1.2, 0.005], nodeDistance: [120, 0, 500, 5], damping: [0.09, 0, 1, 0.01] }, maxVelocity: [50, 0, 150, 1], minVelocity: [0.1, 0.01, 0.5, 0.01], solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'], - timestep: [0.5, 0, 1, 0.05] + timestep: [0.5, 0.01, 1, 0.01] }, global: { locale: ['en', 'nl'] diff --git a/lib/shared/Configurator.js b/lib/shared/Configurator.js index 3862ff0d..2083ef7c 100644 --- a/lib/shared/Configurator.js +++ b/lib/shared/Configurator.js @@ -27,7 +27,8 @@ class Configurator { this.defaultOptions = { enabled: false, filter: true, - container: undefined + container: undefined, + showButton: true }; util.extend(this.options, this.defaultOptions); @@ -61,6 +62,9 @@ class Configurator { if (options.filter !== undefined) { this.options.filter = options.filter; } + if (options.showButton !== undefined) { + this.options.showButton = options.showButton; + } if (options.enabled !== undefined) { enabled = options.enabled; } @@ -107,7 +111,7 @@ class Configurator { show = false; if (typeof filter === 'function') { show = filter(option,[]); - show = show || this._handleObject(this.configureOptions[option], [option]); + show = show || this._handleObject(this.configureOptions[option], [option], true); } else if (filter === true || filter.indexOf(option) !== -1) { show = true; @@ -129,18 +133,21 @@ class Configurator { counter++; } } - let generateButton = document.createElement('div'); - generateButton.className = 'vis-network-configuration button'; - generateButton.innerHTML = 'generate options'; - generateButton.onclick = () => {this._printOptions();}; - generateButton.onmouseover = () => {generateButton.className = 'vis-network-configuration button hover';}; - generateButton.onmouseout = () => {generateButton.className = 'vis-network-configuration button';}; - this.optionsContainer = document.createElement('div'); - this.optionsContainer.className = 'vis-network-configuration vis-option-container'; + if (this.options.showButton === true) { + let generateButton = document.createElement('div'); + generateButton.className = 'vis-network-configuration button'; + generateButton.innerHTML = 'generate options'; + generateButton.onclick = () => {this._printOptions();}; + generateButton.onmouseover = () => {generateButton.className = 'vis-network-configuration button hover';}; + generateButton.onmouseout = () => {generateButton.className = 'vis-network-configuration button';}; + + this.optionsContainer = document.createElement('div'); + this.optionsContainer.className = 'vis-network-configuration vis-option-container'; - this.domElements.push(this.optionsContainer); - this.domElements.push(generateButton); + this.domElements.push(this.optionsContainer); + this.domElements.push(generateButton); + } this._push(); this.colorPicker.insertTo(this.container); @@ -447,24 +454,30 @@ class Configurator { * @param path * @private */ - _handleObject(obj, path = []) { + _handleObject(obj, path = [], checkOnly = false) { let show = false; let filter = this.options.filter; let visibleInSet = false; for (let subObj in obj) { if (obj.hasOwnProperty(subObj)) { - show = false; + show = true; + let item = obj[subObj]; + let newPath = util.copyAndExtendArray(path, subObj); if (typeof filter === 'function') { show = filter(subObj,path); - } - else if (filter === true) { - show = true; + + // if needed we must go deeper into the object. + if (show === false) { + if (!(item instanceof Array) && typeof item !== 'string' && typeof item !== 'boolean' && item instanceof Object) { + this.allowCreation = false; + show = this._handleObject(item, newPath, true); + this.allowCreation = checkOnly === false; + } + } } if (show !== false) { visibleInSet = true; - let item = obj[subObj]; - let newPath = util.copyAndExtendArray(path, subObj); let value = this._getValue(newPath); if (item instanceof Array) {